@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
package/src/login/login.scss
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
@use '@carbon/type';
|
|
3
3
|
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
4
|
|
|
5
|
+
/* Hide OpenMRS top nav on login */
|
|
6
|
+
:global(.hide-top-nav #omrs-top-nav-app-container) {
|
|
7
|
+
display: none;
|
|
8
|
+
height: 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
5
11
|
.bodyShort01 {
|
|
6
12
|
@include type.type-style('body-compact-01');
|
|
7
13
|
}
|
|
@@ -34,10 +40,12 @@
|
|
|
34
40
|
|
|
35
41
|
.container {
|
|
36
42
|
display: flex;
|
|
37
|
-
flex-direction:
|
|
38
|
-
justify-content:
|
|
43
|
+
flex-direction: row;
|
|
44
|
+
justify-content: flex-start;
|
|
39
45
|
align-items: center;
|
|
40
46
|
position: relative;
|
|
47
|
+
margin-left: 10rem;
|
|
48
|
+
min-height: 100vh;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
51
|
.center {
|
|
@@ -45,13 +53,13 @@
|
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
.logo {
|
|
48
|
-
margin-bottom: layout.$spacing-08;
|
|
56
|
+
// margin-bottom: layout.$spacing-08;
|
|
49
57
|
height: layout.$spacing-11;
|
|
50
58
|
width: 16rem;
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
.logoImg {
|
|
54
|
-
margin-bottom: layout.$spacing-
|
|
62
|
+
// margin-bottom: layout.$spacing-03;
|
|
55
63
|
max-width: 100%;
|
|
56
64
|
}
|
|
57
65
|
|
|
@@ -165,3 +173,101 @@
|
|
|
165
173
|
position: absolute;
|
|
166
174
|
pointer-events: none;
|
|
167
175
|
}
|
|
176
|
+
|
|
177
|
+
.wrapperContainer {
|
|
178
|
+
display: flex;
|
|
179
|
+
flex-direction: row;
|
|
180
|
+
gap: 10rem;
|
|
181
|
+
max-width: 100vw;
|
|
182
|
+
max-height: 100vh;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.image {
|
|
186
|
+
width: 100%;
|
|
187
|
+
height: 100%;
|
|
188
|
+
max-width: 100vw;
|
|
189
|
+
max-height: 100vh;
|
|
190
|
+
object-fit: cover;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@media (max-width: 1024px) {
|
|
194
|
+
.image {
|
|
195
|
+
max-width: 40rem;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
@media (max-width: 768px) {
|
|
200
|
+
.image {
|
|
201
|
+
max-width: 100%;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.logoContainer {
|
|
206
|
+
display: flex;
|
|
207
|
+
flex-direction: column;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.poweredBy {
|
|
211
|
+
display: flex;
|
|
212
|
+
align-items: center;
|
|
213
|
+
justify-self: flex-end;
|
|
214
|
+
font-size: 0.75rem;
|
|
215
|
+
color: blue;
|
|
216
|
+
opacity: 0.85;
|
|
217
|
+
white-space: nowrap;
|
|
218
|
+
font-weight: bold;
|
|
219
|
+
margin-bottom: layout.$spacing-08;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.poweredByLogo {
|
|
223
|
+
margin-left: 0.5rem;
|
|
224
|
+
width: 2rem;
|
|
225
|
+
height: auto;
|
|
226
|
+
object-fit: contain;
|
|
227
|
+
border-radius: 0.5rem;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.logoContainer {
|
|
231
|
+
display: flex;
|
|
232
|
+
flex-direction: column;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.logoSection {
|
|
236
|
+
position: fixed;
|
|
237
|
+
bottom: layout.$spacing-01;
|
|
238
|
+
display: flex;
|
|
239
|
+
justify-content: space-between;
|
|
240
|
+
align-items: center;
|
|
241
|
+
padding: 0 layout.$spacing-04;
|
|
242
|
+
gap: clamp(8rem, 27vw, 26rem);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.leftLogos {
|
|
246
|
+
display: flex;
|
|
247
|
+
flex-direction: row;
|
|
248
|
+
gap: 1rem;
|
|
249
|
+
border: 2px solid lightgray;
|
|
250
|
+
border-radius: 0.5rem;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.supportingLogos {
|
|
254
|
+
margin-left: 0.5rem;
|
|
255
|
+
width: 3rem;
|
|
256
|
+
height: auto;
|
|
257
|
+
object-fit: contain;
|
|
258
|
+
border-radius: 0.5rem;
|
|
259
|
+
}
|
|
260
|
+
.dhaLogo {
|
|
261
|
+
margin-left: 0.5rem;
|
|
262
|
+
width: 4rem;
|
|
263
|
+
height: auto;
|
|
264
|
+
object-fit: contain;
|
|
265
|
+
border-radius: 0.5rem;
|
|
266
|
+
}
|
|
267
|
+
.openmsrsLogo {
|
|
268
|
+
margin-left: 0.5rem;
|
|
269
|
+
width: 6rem;
|
|
270
|
+
height: auto;
|
|
271
|
+
object-fit: contain;
|
|
272
|
+
border-radius: 0.5rem;
|
|
273
|
+
}
|
package/src/logo.component.tsx
CHANGED
|
@@ -3,20 +3,21 @@ import { interpolateUrl, useConfig } from '@openmrs/esm-framework';
|
|
|
3
3
|
import { type TFunction } from 'i18next';
|
|
4
4
|
import { type ConfigSchema } from './config-schema';
|
|
5
5
|
import styles from './login/login.scss';
|
|
6
|
+
import taifaCare from './assets/Taifa-Care.png';
|
|
7
|
+
import amrsLogo from './assets/ampath-logo.png';
|
|
6
8
|
|
|
7
9
|
const Logo: React.FC<{ t: TFunction }> = ({ t }) => {
|
|
8
10
|
const { logo } = useConfig<ConfigSchema>();
|
|
9
|
-
return
|
|
10
|
-
<
|
|
11
|
-
alt={logo.alt ? t(logo.alt) : t('openmrsLogo', 'OpenMRS logo')}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
</svg>
|
|
11
|
+
return (
|
|
12
|
+
<div className={styles.logoContainer}>
|
|
13
|
+
<img alt={logo.alt ? t(logo.alt) : t('openmrsLogo', 'OpenMRS logo')} className={styles.logoImg} src={taifaCare} />
|
|
14
|
+
<div>
|
|
15
|
+
<span className={styles.poweredBy}>
|
|
16
|
+
{t('poweredBy', 'Powered by AMRS')}{' '}
|
|
17
|
+
<img src={amrsLogo} alt={t('taifaCare', 'TAIFA CARE logo')} className={styles.poweredByLogo} />
|
|
18
|
+
</span>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
20
21
|
);
|
|
21
22
|
};
|
|
22
23
|
|
|
@@ -1,30 +1,38 @@
|
|
|
1
|
-
import React, { useState } from
|
|
2
|
-
import { Button, InlineNotification, Loading } from
|
|
3
|
-
import { useLocation, useNavigate } from
|
|
4
|
-
import { useTranslation } from
|
|
1
|
+
import React, { useEffect, 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();
|
|
19
20
|
const { t } = useTranslation();
|
|
20
21
|
const location = useLocation();
|
|
21
22
|
|
|
22
|
-
const { username, password } = location.state || {};
|
|
23
|
+
const { username, password, message } = location.state || {};
|
|
23
24
|
|
|
24
25
|
const handleOtpChange = (val: React.SetStateAction<string>) => {
|
|
25
26
|
setOtpValue(val);
|
|
26
27
|
};
|
|
27
28
|
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
document.body.classList.add('hide-top-nav');
|
|
31
|
+
return () => {
|
|
32
|
+
document.body.classList.remove('hide-top-nav');
|
|
33
|
+
};
|
|
34
|
+
}, []);
|
|
35
|
+
|
|
28
36
|
const handleVerify = async () => {
|
|
29
37
|
setIsLoading(true);
|
|
30
38
|
setError(null);
|
|
@@ -36,47 +44,50 @@ const OtpComponent: React.FC = () => {
|
|
|
36
44
|
const session = sessionStore.session;
|
|
37
45
|
|
|
38
46
|
if (!session.sessionLocation) {
|
|
39
|
-
navigate(
|
|
47
|
+
navigate('/login/location');
|
|
40
48
|
return;
|
|
41
49
|
}
|
|
42
50
|
|
|
43
|
-
let to =
|
|
51
|
+
let to = '/home';
|
|
44
52
|
if (location.state?.referrer) {
|
|
45
|
-
to = location.state.referrer
|
|
46
|
-
? `\${openmrsSpaBase}${location.state.referrer}`
|
|
47
|
-
: location.state.referrer;
|
|
53
|
+
to = location.state.referrer;
|
|
48
54
|
}
|
|
55
|
+
// if (location.state?.referrer) {
|
|
56
|
+
// to = location.state.referrer.startsWith('/')
|
|
57
|
+
// ? `\${openmrsSpaBase}${location.state.referrer}`
|
|
58
|
+
// : location.state.referrer;
|
|
59
|
+
// }
|
|
49
60
|
|
|
50
61
|
navigate(to);
|
|
51
62
|
} else {
|
|
52
63
|
setError(res.data.message);
|
|
53
64
|
}
|
|
54
65
|
} catch (error) {
|
|
55
|
-
setError(
|
|
56
|
-
error?.message ||
|
|
57
|
-
error?.attributes?.error ||
|
|
58
|
-
"Invalid OTP or credentials"
|
|
59
|
-
);
|
|
66
|
+
setError(error?.message || error?.attributes?.error || 'Invalid OTP or credentials');
|
|
60
67
|
} finally {
|
|
61
68
|
setIsLoading(false);
|
|
62
69
|
}
|
|
63
70
|
};
|
|
64
71
|
|
|
65
72
|
const handleCancel = () => {
|
|
66
|
-
|
|
73
|
+
const fallback = 'login';
|
|
74
|
+
if (window.history.length > 1) {
|
|
75
|
+
navigate(-1);
|
|
76
|
+
} else {
|
|
77
|
+
navigate(fallback, { replace: true });
|
|
78
|
+
}
|
|
67
79
|
};
|
|
80
|
+
|
|
68
81
|
return (
|
|
69
82
|
<>
|
|
70
83
|
<div className={styles.wrapperContainer}>
|
|
71
|
-
<div>
|
|
84
|
+
<div className={styles.leftSide}>
|
|
72
85
|
<div className={styles.logo}>
|
|
73
86
|
<Logo t={t} />
|
|
74
87
|
</div>
|
|
75
88
|
<div className={styles.container}>
|
|
76
89
|
<h2 className={styles.header}>OTP</h2>
|
|
77
|
-
<p>
|
|
78
|
-
Please Check your email <br /> and enter your One Time Password
|
|
79
|
-
</p>
|
|
90
|
+
<p>{message || 'Enter the OTP sent to your registered email and phone number to complete login.'}</p>
|
|
80
91
|
<OTPInput length={5} onChange={handleOtpChange} />
|
|
81
92
|
{error && (
|
|
82
93
|
<InlineNotification
|
|
@@ -88,15 +99,15 @@ const OtpComponent: React.FC = () => {
|
|
|
88
99
|
/>
|
|
89
100
|
)}
|
|
90
101
|
<Button className={styles.button} onClick={handleVerify}>
|
|
91
|
-
{isLoading ? <Loading /> :
|
|
102
|
+
{isLoading ? <Loading /> : 'Verify'}
|
|
92
103
|
</Button>
|
|
93
104
|
<Button className={styles.button} onClick={handleCancel}>
|
|
94
105
|
Cancel
|
|
95
106
|
</Button>
|
|
96
107
|
<ResendTimer username={username} password={password} />
|
|
97
|
-
<Footer />
|
|
98
108
|
</div>
|
|
99
109
|
</div>
|
|
110
|
+
<img className={styles.image} src={image} alt="TAIFA CARE" />
|
|
100
111
|
</div>
|
|
101
112
|
</>
|
|
102
113
|
);
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
4
|
+
|
|
5
|
+
:global(.hide-top-nav #omrs-top-nav-app-container) {
|
|
6
|
+
display: none;
|
|
7
|
+
height: 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.container {
|
|
11
|
+
display: flex;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
gap: 1rem;
|
|
14
|
+
margin-left: 5rem;
|
|
15
|
+
}
|
|
16
|
+
.leftSide {
|
|
17
|
+
margin-top: clamp(5rem, 10vw, 20rem);
|
|
18
|
+
margin-left: clamp(2rem, 5vw, 10rem);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.button {
|
|
22
|
+
width: 15rem;
|
|
23
|
+
display: flex;
|
|
24
|
+
justify-content: center;
|
|
25
|
+
align-items: center;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.header {
|
|
29
|
+
font-weight: bold;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.logo {
|
|
33
|
+
margin-left: 5rem;
|
|
34
|
+
width: 15rem;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.wrapperContainer {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: row;
|
|
40
|
+
gap: 10rem;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.image {
|
|
44
|
+
width: 100%;
|
|
45
|
+
height: 100%;
|
|
46
|
+
max-width: 100vw;
|
|
47
|
+
max-height: 100vh;
|
|
48
|
+
object-fit: cover;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@media (max-width: 1024px) {
|
|
52
|
+
.image {
|
|
53
|
+
max-width: 40rem;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@media (max-width: 768px) {
|
|
58
|
+
.image {
|
|
59
|
+
max-width: 100%;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -1,45 +1,96 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { openmrsFetch } from '@openmrs/esm-framework';
|
|
2
|
+
import { getEtlBaseUrl, getOtpKey, getSubDomain } from '../utils/get-base-url';
|
|
2
3
|
|
|
3
|
-
const
|
|
4
|
+
const EMAIL_ATTRIBUTE_TYPE_UUID = 'ecabe213-160b-11ef-ad65-a0d3c1fcd41c';
|
|
5
|
+
const PHONE_NUMBER_ATTRIBUTE_TYPE_UUID = '72a759a8-1359-11df-a1f1-0026b9348838';
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
type ContactInfo = {
|
|
8
|
+
email?: string | null;
|
|
9
|
+
phone?: string | null;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export async function getEmailAndPhone(uuid: string, username: string, password: string): Promise<ContactInfo> {
|
|
13
|
+
const subDomain = await getSubDomain();
|
|
8
14
|
const credentials = window.btoa(`${username}:${password}`);
|
|
15
|
+
try {
|
|
16
|
+
if (!uuid) return;
|
|
17
|
+
const url = `${subDomain}/amrs/ws/rest/v1/person/${uuid}?v=custom:attributes`;
|
|
9
18
|
|
|
10
|
-
|
|
19
|
+
const res = await openmrsFetch(url, {
|
|
20
|
+
method: 'GET',
|
|
21
|
+
headers: {
|
|
22
|
+
Authorization: `Basic ${credentials}`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
11
25
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
26
|
+
const data = await res.json();
|
|
27
|
+
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
throw new Error(data.message);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const emailAttr = data.attributes?.find(
|
|
33
|
+
(attr) =>
|
|
34
|
+
!attr.voided && attr.attributeType?.uuid === EMAIL_ATTRIBUTE_TYPE_UUID && typeof attr.value === 'string',
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
const phoneAttr = data.attributes?.find(
|
|
38
|
+
(attr) => !attr.voided && attr.attributeType?.uuid === PHONE_NUMBER_ATTRIBUTE_TYPE_UUID,
|
|
39
|
+
);
|
|
40
|
+
const email = emailAttr?.value ?? null;
|
|
41
|
+
const phone = phoneAttr?.value ?? null;
|
|
42
|
+
|
|
43
|
+
if (!email && !phone) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
'Neither email nor phone number has been configured. Please contact system administrator for assistance.',
|
|
46
|
+
);
|
|
47
|
+
}
|
|
18
48
|
|
|
19
|
-
|
|
20
|
-
|
|
49
|
+
return { email, phone };
|
|
50
|
+
} catch (error) {
|
|
51
|
+
throw new Error(error.message ?? 'Failed to fetch contact info');
|
|
21
52
|
}
|
|
53
|
+
}
|
|
22
54
|
|
|
23
|
-
|
|
55
|
+
export async function getOtp(username: string, password: string, email: string, phone: string) {
|
|
56
|
+
const etlBaseUrl = await getEtlBaseUrl();
|
|
57
|
+
const key = await getOtpKey();
|
|
58
|
+
const params = new URLSearchParams({ username, email, phone, key });
|
|
59
|
+
const credentials = window.btoa(`${username}:${password}`);
|
|
24
60
|
|
|
25
|
-
|
|
61
|
+
try {
|
|
62
|
+
const url = `${etlBaseUrl}/otp?${params.toString()}`;
|
|
63
|
+
|
|
64
|
+
const res = await openmrsFetch(url, {
|
|
65
|
+
method: 'GET',
|
|
66
|
+
headers: {
|
|
67
|
+
Authorization: `Basic ${credentials}`,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const data = await res.json();
|
|
72
|
+
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
throw new Error(data.message);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return data.data;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
throw new Error(error.message);
|
|
80
|
+
}
|
|
26
81
|
}
|
|
27
82
|
|
|
28
|
-
export async function verifyOtp(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
otp: string
|
|
32
|
-
) {
|
|
33
|
-
// const etlBaseUrl = await getEtlBaseUrl();
|
|
34
|
-
const url = etlBaseUrl + "verify-otp";
|
|
83
|
+
export async function verifyOtp(username: string, password: string, otp: string) {
|
|
84
|
+
const etlBaseUrl = await getEtlBaseUrl();
|
|
85
|
+
const url = etlBaseUrl + '/verify-otp';
|
|
35
86
|
const credentials = window.btoa(`${username}:${password}`);
|
|
36
87
|
|
|
37
88
|
const body = { username, otp };
|
|
38
89
|
|
|
39
|
-
const res = await
|
|
40
|
-
method:
|
|
90
|
+
const res = await openmrsFetch(url, {
|
|
91
|
+
method: 'POST',
|
|
41
92
|
headers: {
|
|
42
|
-
|
|
93
|
+
'Content-Type': 'Application/json',
|
|
43
94
|
Authorization: `Basic ${credentials}`,
|
|
44
95
|
},
|
|
45
96
|
body: JSON.stringify(body),
|
package/src/root.component.tsx
CHANGED
|
@@ -5,6 +5,8 @@ import LocationPickerView from "./location-picker/location-picker-view.component
|
|
|
5
5
|
import Login from "./login/login.component";
|
|
6
6
|
import RedirectLogout from "./redirect-logout/redirect-logout.component";
|
|
7
7
|
import OtpComponent from "./otp/otp.component";
|
|
8
|
+
import ForgotPassword from "./forgot-password/forgot-password.component";
|
|
9
|
+
import ResetPassword from "./forgot-password/reset-password/reset-password.component";
|
|
8
10
|
|
|
9
11
|
const Root: React.FC = () => {
|
|
10
12
|
return (
|
|
@@ -16,6 +18,8 @@ const Root: React.FC = () => {
|
|
|
16
18
|
<Route path="logout" element={<RedirectLogout />} />
|
|
17
19
|
<Route path="change-password" element={<ChangePassword />} />
|
|
18
20
|
<Route path="login/otp" element={<OtpComponent />} />
|
|
21
|
+
<Route path="login/forgot-password" element={<ForgotPassword />} />
|
|
22
|
+
<Route path="login/reset-password/:activationKey" element={<ResetPassword />} />
|
|
19
23
|
</Routes>
|
|
20
24
|
</BrowserRouter>
|
|
21
25
|
);
|
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import { getConfig } from
|
|
2
|
-
import { moduleName } from
|
|
1
|
+
import { getConfig } from '@openmrs/esm-framework';
|
|
2
|
+
import { moduleName } from '../';
|
|
3
3
|
|
|
4
4
|
export async function getEtlBaseUrl() {
|
|
5
5
|
const { etlBaseUrl } = await getConfig(moduleName);
|
|
6
6
|
return etlBaseUrl ?? null;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
export async function getOtpEnabledStatus() {
|
|
10
|
+
const { enabled } = await getConfig(moduleName);
|
|
11
|
+
return enabled ?? null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function getSubDomain() {
|
|
15
|
+
const { subDomain } = await getConfig(moduleName);
|
|
16
|
+
return subDomain ?? null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function getOtpKey() {
|
|
20
|
+
const { otpKey } = await getConfig(moduleName);
|
|
21
|
+
return otpKey ?? null;
|
|
22
|
+
}
|
package/yarnrc.yml
CHANGED
|
@@ -4,11 +4,11 @@ enableGlobalCache: false
|
|
|
4
4
|
|
|
5
5
|
nodeLinker: node-modules
|
|
6
6
|
|
|
7
|
-
npmPublishRegistry:
|
|
7
|
+
npmPublishRegistry: 'https://registry.npmjs.org'
|
|
8
8
|
|
|
9
9
|
plugins:
|
|
10
10
|
- checksum: c13ed363e15a826d9f779e7e7aca9dbee1d6d54813261d6f495da2fa94b01fa7579e516587ae2df5834f5d63d5d90cb392135190e8878b81dbd830c3c9f57809
|
|
11
11
|
path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs
|
|
12
|
-
spec:
|
|
12
|
+
spec: 'https://go.mskelton.dev/yarn-outdated/v4'
|
|
13
13
|
|
|
14
14
|
yarnPath: .yarn/releases/yarn-4.10.3.cjs
|