@ampath/esm-login-app 8.0.0-next.4 → 8.0.0-next.6
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/7853.js +1 -0
- package/dist/7853.js.map +1 -0
- package/dist/d0bf081185f017f3.jpg +0 -0
- package/dist/esm-login-app.js +1 -1
- package/dist/esm-login-app.js.buildmanifest.json +32 -28
- package/dist/esm-login-app.js.map +1 -1
- 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/medicine.jpg +0 -0
- package/src/common/resend-timer/resend-timer.component.tsx +5 -1
- package/src/config-schema.ts +22 -37
- package/src/declarations.d.ts +4 -0
- package/src/login/login.component.tsx +133 -115
- package/src/login/login.scss +16 -2
- package/src/otp/otp.component.tsx +27 -24
- package/src/otp/{otp.scss → otp.module.scss} +11 -13
- package/src/resources/otp.resource.ts +60 -15
- package/src/utils/get-base-url.ts +5 -0
- package/dist/2310.js +0 -1
- package/dist/2310.js.map +0 -1
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.6"}
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import styles from './resend-timer.scss';
|
|
3
3
|
import { getOtp } from '../../resources/otp.resource';
|
|
4
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
4
5
|
|
|
5
6
|
interface ResendTimerProps {
|
|
6
7
|
username: string;
|
|
@@ -10,6 +11,9 @@ interface ResendTimerProps {
|
|
|
10
11
|
const ResendTimer: React.FC<ResendTimerProps> = ({ username, password }) => {
|
|
11
12
|
const RESEND_SECONDS = 30;
|
|
12
13
|
const [secondsLeft, setSecondsLeft] = useState(RESEND_SECONDS);
|
|
14
|
+
const session = useSession();
|
|
15
|
+
|
|
16
|
+
const uuid = session.user.person.uuid;
|
|
13
17
|
|
|
14
18
|
useEffect(() => {
|
|
15
19
|
if (secondsLeft === 0) return;
|
|
@@ -27,7 +31,7 @@ const ResendTimer: React.FC<ResendTimerProps> = ({ username, password }) => {
|
|
|
27
31
|
};
|
|
28
32
|
|
|
29
33
|
const handleResend = async () => {
|
|
30
|
-
await getOtp(username, password);
|
|
34
|
+
await getOtp(username, password, uuid);
|
|
31
35
|
setSecondsLeft(RESEND_SECONDS);
|
|
32
36
|
};
|
|
33
37
|
return (
|
package/src/config-schema.ts
CHANGED
|
@@ -1,27 +1,25 @@
|
|
|
1
|
-
import { validators, Type, validator } from
|
|
1
|
+
import { validators, Type, validator } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
3
|
export const configSchema = {
|
|
4
4
|
provider: {
|
|
5
5
|
type: {
|
|
6
6
|
_type: Type.String,
|
|
7
|
-
_default:
|
|
7
|
+
_default: 'basic',
|
|
8
8
|
_description:
|
|
9
9
|
"Selects the login mechanism to use. Choices are 'basic' and 'oauth2'. " +
|
|
10
10
|
"For 'oauth2' you'll also need to set the 'loginUrl'",
|
|
11
|
-
_validators: [validators.oneOf([
|
|
11
|
+
_validators: [validators.oneOf(['basic', 'oauth2'])],
|
|
12
12
|
},
|
|
13
13
|
loginUrl: {
|
|
14
14
|
_type: Type.String,
|
|
15
|
-
_default:
|
|
16
|
-
_description:
|
|
17
|
-
"The URL to use to login. This is only needed if you are using OAuth2.",
|
|
15
|
+
_default: '${openmrsSpaBase}/login',
|
|
16
|
+
_description: 'The URL to use to login. This is only needed if you are using OAuth2.',
|
|
18
17
|
_validators: [validators.isUrl],
|
|
19
18
|
},
|
|
20
19
|
logoutUrl: {
|
|
21
20
|
_type: Type.String,
|
|
22
|
-
_default:
|
|
23
|
-
_description:
|
|
24
|
-
"The URL to use to login. This is only needed if you are using OAuth2.",
|
|
21
|
+
_default: '${openmrsSpaBase}/logout',
|
|
22
|
+
_description: 'The URL to use to login. This is only needed if you are using OAuth2.',
|
|
25
23
|
_validators: [validators.isUrl],
|
|
26
24
|
},
|
|
27
25
|
},
|
|
@@ -36,25 +34,14 @@ export const configSchema = {
|
|
|
36
34
|
numberToShow: {
|
|
37
35
|
_type: Type.Number,
|
|
38
36
|
_default: 8,
|
|
39
|
-
_description:
|
|
40
|
-
_validators: [
|
|
41
|
-
validator(
|
|
42
|
-
(v: unknown) => typeof v === "number" && v > 0,
|
|
43
|
-
"Must be greater than zero"
|
|
44
|
-
),
|
|
45
|
-
],
|
|
37
|
+
_description: 'The number of locations displayed in the location picker.',
|
|
38
|
+
_validators: [validator((v: unknown) => typeof v === 'number' && v > 0, 'Must be greater than zero')],
|
|
46
39
|
},
|
|
47
40
|
locationsPerRequest: {
|
|
48
41
|
_type: Type.Number,
|
|
49
42
|
_default: 50,
|
|
50
|
-
_description:
|
|
51
|
-
|
|
52
|
-
_validators: [
|
|
53
|
-
validator(
|
|
54
|
-
(v: unknown) => typeof v === "number" && v > 0,
|
|
55
|
-
"Must be greater than zero"
|
|
56
|
-
),
|
|
57
|
-
],
|
|
43
|
+
_description: 'The number of results to fetch in each cycle of infinite scroll.',
|
|
44
|
+
_validators: [validator((v: unknown) => typeof v === 'number' && v > 0, 'Must be greater than zero')],
|
|
58
45
|
},
|
|
59
46
|
useLoginLocationTag: {
|
|
60
47
|
_type: Type.Boolean,
|
|
@@ -66,24 +53,23 @@ export const configSchema = {
|
|
|
66
53
|
links: {
|
|
67
54
|
loginSuccess: {
|
|
68
55
|
_type: Type.String,
|
|
69
|
-
_default:
|
|
70
|
-
_description:
|
|
56
|
+
_default: '${openmrsSpaBase}/home',
|
|
57
|
+
_description: 'The URL to redirect the user to after a successful login.',
|
|
71
58
|
_validators: [validators.isUrl],
|
|
72
59
|
},
|
|
73
60
|
},
|
|
74
61
|
logo: {
|
|
75
62
|
src: {
|
|
76
63
|
_type: Type.String,
|
|
77
|
-
_default:
|
|
64
|
+
_default: '',
|
|
78
65
|
_description:
|
|
79
|
-
|
|
66
|
+
'The path or URL to the logo image. If set to an empty string, the default OpenMRS SVG sprite will be used.',
|
|
80
67
|
_validators: [validators.isUrl],
|
|
81
68
|
},
|
|
82
69
|
alt: {
|
|
83
70
|
_type: Type.String,
|
|
84
|
-
_default:
|
|
85
|
-
_description:
|
|
86
|
-
"The alternative text for the logo image, displayed when the image cannot be loaded or on hover.",
|
|
71
|
+
_default: 'Logo',
|
|
72
|
+
_description: 'The alternative text for the logo image, displayed when the image cannot be loaded or on hover.',
|
|
87
73
|
},
|
|
88
74
|
},
|
|
89
75
|
footer: {
|
|
@@ -94,25 +80,24 @@ export const configSchema = {
|
|
|
94
80
|
src: {
|
|
95
81
|
_type: Type.String,
|
|
96
82
|
_required: true,
|
|
97
|
-
_description:
|
|
83
|
+
_description: 'The source URL of the logo image',
|
|
98
84
|
_validators: [validators.isUrl],
|
|
99
85
|
},
|
|
100
86
|
alt: {
|
|
101
87
|
_type: Type.String,
|
|
102
88
|
_required: true,
|
|
103
|
-
_description:
|
|
89
|
+
_description: 'The alternative text for the logo image',
|
|
104
90
|
},
|
|
105
91
|
},
|
|
106
92
|
_default: [],
|
|
107
|
-
_description:
|
|
108
|
-
"An array of logos to be displayed in the footer next to the OpenMRS logo.",
|
|
93
|
+
_description: 'An array of logos to be displayed in the footer next to the OpenMRS logo.',
|
|
109
94
|
},
|
|
110
95
|
},
|
|
111
96
|
showPasswordOnSeparateScreen: {
|
|
112
97
|
_type: Type.Boolean,
|
|
113
98
|
_default: false,
|
|
114
99
|
_description:
|
|
115
|
-
|
|
100
|
+
'Whether to show the password field on a separate screen. If false, the password field will be shown on the same screen.',
|
|
116
101
|
},
|
|
117
102
|
};
|
|
118
103
|
|
|
@@ -139,7 +124,7 @@ export interface ConfigSchema {
|
|
|
139
124
|
provider: {
|
|
140
125
|
loginUrl: string;
|
|
141
126
|
logoutUrl: string;
|
|
142
|
-
type:
|
|
127
|
+
type: 'basic' | 'oauth2';
|
|
143
128
|
};
|
|
144
129
|
showPasswordOnSeparateScreen: boolean;
|
|
145
130
|
}
|
package/src/declarations.d.ts
CHANGED
|
@@ -13,11 +13,10 @@ import {
|
|
|
13
13
|
} from '@openmrs/esm-framework';
|
|
14
14
|
import { type ConfigSchema } from '../config-schema';
|
|
15
15
|
import Logo from '../logo.component';
|
|
16
|
-
import Footer from '../footer.component';
|
|
17
16
|
import styles from './login.scss';
|
|
18
|
-
import { getOtp } from '../resources/otp.resource';
|
|
17
|
+
import { getEmail, getOtp } from '../resources/otp.resource';
|
|
19
18
|
import { getOtpEnabledStatus } from '../utils/get-base-url';
|
|
20
|
-
import
|
|
19
|
+
import image from '../assets/medicine.jpg';
|
|
21
20
|
|
|
22
21
|
export interface LoginReferrer {
|
|
23
22
|
referrer?: string;
|
|
@@ -87,8 +86,6 @@ const Login: React.FC = () => {
|
|
|
87
86
|
const currentPassword = passwordInputRef.current?.value || password;
|
|
88
87
|
const isOtpEnabled: boolean = await getOtpEnabledStatus();
|
|
89
88
|
|
|
90
|
-
console.log('OTPSTATUS: ', isOtpEnabled);
|
|
91
|
-
|
|
92
89
|
if (showPasswordOnSeparateScreen && !showPasswordField) {
|
|
93
90
|
continueLogin();
|
|
94
91
|
return false;
|
|
@@ -105,7 +102,7 @@ const Login: React.FC = () => {
|
|
|
105
102
|
const session = sessionStore.session;
|
|
106
103
|
const authenticated = sessionStore?.session?.authenticated;
|
|
107
104
|
|
|
108
|
-
if (isOtpEnabled
|
|
105
|
+
if (isOtpEnabled !== true) {
|
|
109
106
|
if (authenticated) {
|
|
110
107
|
if (session.sessionLocation) {
|
|
111
108
|
let to = loginLinks?.loginSuccess || '/home';
|
|
@@ -116,7 +113,15 @@ const Login: React.FC = () => {
|
|
|
116
113
|
to = location.state.referrer;
|
|
117
114
|
}
|
|
118
115
|
}
|
|
119
|
-
|
|
116
|
+
const uuid = session.user.person.uuid;
|
|
117
|
+
try {
|
|
118
|
+
const email = await getEmail(uuid, username, password);
|
|
119
|
+
await getOtp(username, password, email);
|
|
120
|
+
} catch (err: any) {
|
|
121
|
+
setErrorMessage(err.message);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
120
125
|
navigate('otp', {
|
|
121
126
|
state: {
|
|
122
127
|
username,
|
|
@@ -125,7 +130,13 @@ const Login: React.FC = () => {
|
|
|
125
130
|
},
|
|
126
131
|
});
|
|
127
132
|
} else if (!session.sessionLocation) {
|
|
128
|
-
|
|
133
|
+
const uuid = session.user.person.uuid;
|
|
134
|
+
try {
|
|
135
|
+
await getOtp(username, password, uuid);
|
|
136
|
+
} catch (err: any) {
|
|
137
|
+
setErrorMessage(err.message);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
129
140
|
navigate('otp', {
|
|
130
141
|
state: {
|
|
131
142
|
username,
|
|
@@ -199,117 +210,124 @@ const Login: React.FC = () => {
|
|
|
199
210
|
|
|
200
211
|
if (!loginProvider || loginProvider.type === 'basic') {
|
|
201
212
|
return (
|
|
202
|
-
|
|
203
|
-
<
|
|
204
|
-
{
|
|
205
|
-
<
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
213
|
+
<>
|
|
214
|
+
<div className={styles.wrapperContainer}>
|
|
215
|
+
<div className={styles.container}>
|
|
216
|
+
<Tile className={styles.loginCard}>
|
|
217
|
+
{errorMessage && (
|
|
218
|
+
<div className={styles.errorMessage}>
|
|
219
|
+
<InlineNotification
|
|
220
|
+
kind="error"
|
|
221
|
+
subtitle={errorMessage}
|
|
222
|
+
title={getCoreTranslation('error')}
|
|
223
|
+
onClick={() => setErrorMessage('')}
|
|
224
|
+
/>
|
|
225
|
+
</div>
|
|
226
|
+
)}
|
|
227
|
+
<div className={styles.center}>
|
|
228
|
+
<Logo t={t} />
|
|
229
|
+
</div>
|
|
230
|
+
<form onSubmit={handleSubmit}>
|
|
231
|
+
<div className={styles.inputGroup}>
|
|
232
|
+
<TextInput
|
|
233
|
+
id="username"
|
|
234
|
+
type="text"
|
|
235
|
+
name="username"
|
|
236
|
+
autoComplete="username"
|
|
237
|
+
labelText={t('username', 'Username')}
|
|
238
|
+
value={username}
|
|
239
|
+
onChange={changeUsername}
|
|
240
|
+
ref={usernameInputRef}
|
|
241
|
+
required
|
|
242
|
+
autoFocus
|
|
243
|
+
/>
|
|
244
|
+
{showPasswordOnSeparateScreen ? (
|
|
245
|
+
<>
|
|
246
|
+
<div className={showPasswordField ? undefined : styles.hiddenPasswordField}>
|
|
247
|
+
<PasswordInput
|
|
248
|
+
id="password"
|
|
249
|
+
labelText={t('password', 'Password')}
|
|
250
|
+
name="password"
|
|
251
|
+
autoComplete="current-password"
|
|
252
|
+
onChange={changePassword}
|
|
253
|
+
ref={passwordInputRef}
|
|
254
|
+
required
|
|
255
|
+
value={password}
|
|
256
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
257
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
258
|
+
aria-hidden={!showPasswordField}
|
|
259
|
+
tabIndex={showPasswordField ? 0 : -1}
|
|
260
|
+
/>
|
|
261
|
+
</div>
|
|
262
|
+
{showPasswordField ? (
|
|
263
|
+
<Button
|
|
264
|
+
type="submit"
|
|
265
|
+
className={styles.continueButton}
|
|
266
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
267
|
+
iconDescription={t('loginButtonIconDescription', 'Log in button')}
|
|
268
|
+
disabled={!isLoginEnabled || isLoggingIn}
|
|
269
|
+
>
|
|
270
|
+
{isLoggingIn ? (
|
|
271
|
+
<InlineLoading
|
|
272
|
+
className={styles.loader}
|
|
273
|
+
description={t('loggingIn', 'Logging in') + '...'}
|
|
274
|
+
/>
|
|
275
|
+
) : (
|
|
276
|
+
t('login', 'Log in')
|
|
277
|
+
)}
|
|
278
|
+
</Button>
|
|
259
279
|
) : (
|
|
260
|
-
|
|
280
|
+
<Button
|
|
281
|
+
type="submit"
|
|
282
|
+
className={styles.continueButton}
|
|
283
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
284
|
+
iconDescription="Continue to password"
|
|
285
|
+
onClick={(evt) => {
|
|
286
|
+
evt.preventDefault();
|
|
287
|
+
continueLogin();
|
|
288
|
+
}}
|
|
289
|
+
disabled={!isLoginEnabled}
|
|
290
|
+
>
|
|
291
|
+
{t('continue', 'Continue')}
|
|
292
|
+
</Button>
|
|
261
293
|
)}
|
|
262
|
-
|
|
294
|
+
</>
|
|
263
295
|
) : (
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
296
|
+
<>
|
|
297
|
+
<PasswordInput
|
|
298
|
+
id="password"
|
|
299
|
+
labelText={t('password', 'Password')}
|
|
300
|
+
name="password"
|
|
301
|
+
autoComplete="current-password"
|
|
302
|
+
onChange={changePassword}
|
|
303
|
+
ref={passwordInputRef}
|
|
304
|
+
required
|
|
305
|
+
value={password}
|
|
306
|
+
showPasswordLabel={t('showPassword', 'Show password')}
|
|
307
|
+
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
308
|
+
/>
|
|
309
|
+
<Button
|
|
310
|
+
type="submit"
|
|
311
|
+
className={styles.continueButton}
|
|
312
|
+
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
313
|
+
iconDescription="Log in"
|
|
314
|
+
disabled={!isLoginEnabled || isLoggingIn}
|
|
315
|
+
>
|
|
316
|
+
{isLoggingIn ? (
|
|
317
|
+
<InlineLoading className={styles.loader} description={t('loggingIn', 'Logging in') + '...'} />
|
|
318
|
+
) : (
|
|
319
|
+
t('login', 'Log in')
|
|
320
|
+
)}
|
|
321
|
+
</Button>
|
|
322
|
+
</>
|
|
277
323
|
)}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
autoComplete="current-password"
|
|
286
|
-
onChange={changePassword}
|
|
287
|
-
ref={passwordInputRef}
|
|
288
|
-
required
|
|
289
|
-
value={password}
|
|
290
|
-
showPasswordLabel={t('showPassword', 'Show password')}
|
|
291
|
-
invalidText={t('validValueRequired', 'A valid value is required')}
|
|
292
|
-
/>
|
|
293
|
-
<Button
|
|
294
|
-
type="submit"
|
|
295
|
-
className={styles.continueButton}
|
|
296
|
-
renderIcon={(props) => <ArrowRightIcon size={24} {...props} />}
|
|
297
|
-
iconDescription="Log in"
|
|
298
|
-
disabled={!isLoginEnabled || isLoggingIn}
|
|
299
|
-
>
|
|
300
|
-
{isLoggingIn ? (
|
|
301
|
-
<InlineLoading className={styles.loader} description={t('loggingIn', 'Logging in') + '...'} />
|
|
302
|
-
) : (
|
|
303
|
-
t('login', 'Log in')
|
|
304
|
-
)}
|
|
305
|
-
</Button>
|
|
306
|
-
</>
|
|
307
|
-
)}
|
|
308
|
-
</div>
|
|
309
|
-
</form>
|
|
310
|
-
</Tile>
|
|
311
|
-
<Footer />
|
|
312
|
-
</div>
|
|
324
|
+
</div>
|
|
325
|
+
</form>
|
|
326
|
+
</Tile>
|
|
327
|
+
</div>
|
|
328
|
+
<img className={styles.image} src={image} alt="TAIFA CARE" />
|
|
329
|
+
</div>
|
|
330
|
+
</>
|
|
313
331
|
);
|
|
314
332
|
}
|
|
315
333
|
return null;
|
package/src/login/login.scss
CHANGED
|
@@ -34,10 +34,11 @@
|
|
|
34
34
|
|
|
35
35
|
.container {
|
|
36
36
|
display: flex;
|
|
37
|
-
flex-direction:
|
|
38
|
-
justify-content:
|
|
37
|
+
flex-direction: row;
|
|
38
|
+
justify-content: flex-start;
|
|
39
39
|
align-items: center;
|
|
40
40
|
position: relative;
|
|
41
|
+
margin-left: 10rem;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
.center {
|
|
@@ -165,3 +166,16 @@
|
|
|
165
166
|
position: absolute;
|
|
166
167
|
pointer-events: none;
|
|
167
168
|
}
|
|
169
|
+
|
|
170
|
+
.wrapperContainer {
|
|
171
|
+
display: flex;
|
|
172
|
+
flex-direction: row;
|
|
173
|
+
gap: 10rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.image {
|
|
177
|
+
width: 50rem;
|
|
178
|
+
height: 55rem;
|
|
179
|
+
max-height: 60rem;
|
|
180
|
+
object-fit: cover;
|
|
181
|
+
}
|
|
@@ -1,18 +1,19 @@
|
|
|
1
|
-
import React, { useState } from
|
|
2
|
-
import { Button, InlineNotification, Loading } from
|
|
3
|
-
import { useLocation, useNavigate } from
|
|
4
|
-
import { useTranslation } from
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { Button, InlineNotification, Loading } from '@carbon/react';
|
|
3
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
5
|
|
|
6
|
-
import styles from
|
|
7
|
-
import OTPInput from
|
|
8
|
-
import ResendTimer from
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
|
|
6
|
+
import styles from './otp.module.scss';
|
|
7
|
+
import OTPInput from '../common/otp/otp.component';
|
|
8
|
+
import ResendTimer from '../common/resend-timer/resend-timer.component';
|
|
9
|
+
import Logo from '../logo.component';
|
|
10
|
+
import { verifyOtp } from '../resources/otp.resource';
|
|
11
|
+
import { refetchCurrentUser } from '@openmrs/esm-framework';
|
|
12
|
+
|
|
13
|
+
import image from '../assets/medicine.jpg';
|
|
13
14
|
|
|
14
15
|
const OtpComponent: React.FC = () => {
|
|
15
|
-
const [otpValue, setOtpValue] = useState(
|
|
16
|
+
const [otpValue, setOtpValue] = useState('');
|
|
16
17
|
const [isLoading, setIsLoading] = useState(false);
|
|
17
18
|
const [error, setError] = useState<string | null>(null);
|
|
18
19
|
const navigate = useNavigate();
|
|
@@ -36,13 +37,13 @@ const OtpComponent: React.FC = () => {
|
|
|
36
37
|
const session = sessionStore.session;
|
|
37
38
|
|
|
38
39
|
if (!session.sessionLocation) {
|
|
39
|
-
navigate(
|
|
40
|
+
navigate('/login/location');
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
let to =
|
|
44
|
+
let to = '/home';
|
|
44
45
|
if (location.state?.referrer) {
|
|
45
|
-
to = location.state.referrer.startsWith(
|
|
46
|
+
to = location.state.referrer.startsWith('/')
|
|
46
47
|
? `\${openmrsSpaBase}${location.state.referrer}`
|
|
47
48
|
: location.state.referrer;
|
|
48
49
|
}
|
|
@@ -52,23 +53,25 @@ const OtpComponent: React.FC = () => {
|
|
|
52
53
|
setError(res.data.message);
|
|
53
54
|
}
|
|
54
55
|
} catch (error) {
|
|
55
|
-
setError(
|
|
56
|
-
error?.message ||
|
|
57
|
-
error?.attributes?.error ||
|
|
58
|
-
"Invalid OTP or credentials"
|
|
59
|
-
);
|
|
56
|
+
setError(error?.message || error?.attributes?.error || 'Invalid OTP or credentials');
|
|
60
57
|
} finally {
|
|
61
58
|
setIsLoading(false);
|
|
62
59
|
}
|
|
63
60
|
};
|
|
64
61
|
|
|
65
62
|
const handleCancel = () => {
|
|
66
|
-
|
|
63
|
+
const fallback = 'login';
|
|
64
|
+
if (window.history.length > 1) {
|
|
65
|
+
navigate(-1);
|
|
66
|
+
} else {
|
|
67
|
+
navigate(fallback, { replace: true });
|
|
68
|
+
}
|
|
67
69
|
};
|
|
70
|
+
|
|
68
71
|
return (
|
|
69
72
|
<>
|
|
70
73
|
<div className={styles.wrapperContainer}>
|
|
71
|
-
<div>
|
|
74
|
+
<div className={styles.leftSide}>
|
|
72
75
|
<div className={styles.logo}>
|
|
73
76
|
<Logo t={t} />
|
|
74
77
|
</div>
|
|
@@ -88,15 +91,15 @@ const OtpComponent: React.FC = () => {
|
|
|
88
91
|
/>
|
|
89
92
|
)}
|
|
90
93
|
<Button className={styles.button} onClick={handleVerify}>
|
|
91
|
-
{isLoading ? <Loading /> :
|
|
94
|
+
{isLoading ? <Loading /> : 'Verify'}
|
|
92
95
|
</Button>
|
|
93
96
|
<Button className={styles.button} onClick={handleCancel}>
|
|
94
97
|
Cancel
|
|
95
98
|
</Button>
|
|
96
99
|
<ResendTimer username={username} password={password} />
|
|
97
|
-
<Footer />
|
|
98
100
|
</div>
|
|
99
101
|
</div>
|
|
102
|
+
<img className={styles.image} src={image} alt="TAIFA CARE" />
|
|
100
103
|
</div>
|
|
101
104
|
</>
|
|
102
105
|
);
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
gap: 1rem;
|
|
9
9
|
margin-left: 5rem;
|
|
10
10
|
}
|
|
11
|
+
.leftSide {
|
|
12
|
+
margin-top: 10rem;
|
|
13
|
+
margin-left: 10rem;
|
|
14
|
+
}
|
|
11
15
|
|
|
12
16
|
.button {
|
|
13
17
|
width: 15rem;
|
|
@@ -27,20 +31,14 @@
|
|
|
27
31
|
|
|
28
32
|
.wrapperContainer {
|
|
29
33
|
display: flex;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
.rightSide {
|
|
34
|
-
display: flex;
|
|
35
|
-
justify-content: center;
|
|
36
|
-
align-items: center;
|
|
37
|
-
width: 50rem;
|
|
34
|
+
flex-direction: row;
|
|
35
|
+
gap: 10rem;
|
|
38
36
|
}
|
|
39
37
|
|
|
40
38
|
.image {
|
|
41
|
-
width:
|
|
42
|
-
height:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
width: 54rem;
|
|
40
|
+
height: 55rem;
|
|
41
|
+
max-height: 60rem;
|
|
42
|
+
object-fit: cover;
|
|
43
|
+
flex-shrink: 0;
|
|
46
44
|
}
|