@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/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.21"}
|
package/package.json
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import styles from './resend-timer.scss';
|
|
3
|
-
import { getOtp } from '../../resources/otp.resource';
|
|
3
|
+
import { getEmailAndPhone, 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,8 @@ const ResendTimer: React.FC<ResendTimerProps> = ({ username, password }) => {
|
|
|
27
31
|
};
|
|
28
32
|
|
|
29
33
|
const handleResend = async () => {
|
|
30
|
-
await
|
|
34
|
+
const { email, phone } = await getEmailAndPhone(uuid, username, password);
|
|
35
|
+
await getOtp(username, password, email, phone);
|
|
31
36
|
setSecondsLeft(RESEND_SECONDS);
|
|
32
37
|
};
|
|
33
38
|
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,34 @@ 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.',
|
|
101
|
+
},
|
|
102
|
+
subDomainUrl: {
|
|
103
|
+
_type: Type.String,
|
|
104
|
+
_default: '',
|
|
105
|
+
_description: 'The subdomain URL for the application.',
|
|
106
|
+
},
|
|
107
|
+
etlBaseUrl: {
|
|
108
|
+
_type: Type.String,
|
|
109
|
+
_default: '',
|
|
110
|
+
_description: 'The ETL base URL for the application.',
|
|
116
111
|
},
|
|
117
112
|
};
|
|
118
113
|
|
|
@@ -139,7 +134,7 @@ export interface ConfigSchema {
|
|
|
139
134
|
provider: {
|
|
140
135
|
loginUrl: string;
|
|
141
136
|
logoutUrl: string;
|
|
142
|
-
type:
|
|
137
|
+
type: 'basic' | 'oauth2';
|
|
143
138
|
};
|
|
144
139
|
showPasswordOnSeparateScreen: boolean;
|
|
145
140
|
}
|
package/src/declarations.d.ts
CHANGED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
5
|
+
import { Controller, FieldError, useForm, type SubmitHandler } from 'react-hook-form';
|
|
6
|
+
import { Button, Form, TextInput, InlineLoading, Tile } from '@carbon/react';
|
|
7
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
8
|
+
import { initiatePasswordReset } from './forgot-password.resource';
|
|
9
|
+
import Logo from '../logo.component';
|
|
10
|
+
import styles from './forgot-password.scss';
|
|
11
|
+
import { Link, useNavigate } from "react-router-dom";
|
|
12
|
+
|
|
13
|
+
const ForgotPassword: React.FC = () => {
|
|
14
|
+
const { t } = useTranslation();
|
|
15
|
+
const navigate = useNavigate();
|
|
16
|
+
const [initiatingPasswordReset, setIsInitiatingPasswordReset] = useState(false);
|
|
17
|
+
|
|
18
|
+
const usernameOrEmailValidation = z.string({
|
|
19
|
+
required_error: t('usernameOrEmailRequired', 'Username or Email is required'),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const resetPasswordFormSchema = z
|
|
23
|
+
.object({
|
|
24
|
+
usernameOrEmail: usernameOrEmailValidation,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
handleSubmit,
|
|
29
|
+
control,
|
|
30
|
+
formState: { errors },
|
|
31
|
+
} = useForm({
|
|
32
|
+
resolver: zodResolver(resetPasswordFormSchema),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const onSubmit: SubmitHandler<z.infer<typeof resetPasswordFormSchema>> = useCallback(
|
|
36
|
+
(data) => {
|
|
37
|
+
setIsInitiatingPasswordReset(true);
|
|
38
|
+
|
|
39
|
+
const { usernameOrEmail } = data;
|
|
40
|
+
|
|
41
|
+
initiatePasswordReset(usernameOrEmail)
|
|
42
|
+
.then(() => {
|
|
43
|
+
showSnackbar({
|
|
44
|
+
title: t('passwordResetSuccessfull', 'Password reset successfull. Reset link has been sent to your email.'),
|
|
45
|
+
kind: 'success',
|
|
46
|
+
});
|
|
47
|
+
navigate('/login');
|
|
48
|
+
})
|
|
49
|
+
.catch((error) => {
|
|
50
|
+
let errorMessage = error?.responseBody?.error?.rawMessage ?? "";
|
|
51
|
+
if(errorMessage.includes("recipient")) {
|
|
52
|
+
errorMessage = t('recipientAddressNotConfigured', 'Recipient email address not configured.');
|
|
53
|
+
}
|
|
54
|
+
showSnackbar({
|
|
55
|
+
kind: 'error',
|
|
56
|
+
subtitle: errorMessage,
|
|
57
|
+
title: t('errorResettingPassword', 'Error resetting password'),
|
|
58
|
+
});
|
|
59
|
+
})
|
|
60
|
+
.finally(() => {
|
|
61
|
+
setIsInitiatingPasswordReset(false);
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
[t],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const onError = useCallback(() => setIsInitiatingPasswordReset(false), []);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className={styles.container}>
|
|
71
|
+
<Tile className={styles.changePasswordCard}>
|
|
72
|
+
<div className={styles.alignCenter}>
|
|
73
|
+
<Logo t={t} />
|
|
74
|
+
</div>
|
|
75
|
+
<Form onSubmit={handleSubmit(onSubmit, onError)}>
|
|
76
|
+
<Controller
|
|
77
|
+
name="usernameOrEmail"
|
|
78
|
+
control={control}
|
|
79
|
+
render={({ field: { onChange, value } }) => (
|
|
80
|
+
<TextInput
|
|
81
|
+
id="usernameOrEmail"
|
|
82
|
+
invalid={!!errors?.usernameOrEmail}
|
|
83
|
+
invalidText={
|
|
84
|
+
(errors &&
|
|
85
|
+
errors.usernameOrEmail &&
|
|
86
|
+
errors.usernameOrEmail.message &&
|
|
87
|
+
typeof errors.usernameOrEmail.message === 'string' &&
|
|
88
|
+
errors.usernameOrEmail.message) ??
|
|
89
|
+
''
|
|
90
|
+
}
|
|
91
|
+
labelText={t('usernameOrEmail', 'Username or Email')}
|
|
92
|
+
onChange={onChange}
|
|
93
|
+
value={value}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
/>
|
|
97
|
+
<Button className={styles.submitButton} disabled={initiatingPasswordReset} type="submit">
|
|
98
|
+
{initiatingPasswordReset ? (
|
|
99
|
+
<InlineLoading description={t('resettingPassword', 'Resetting password') + '...'} />
|
|
100
|
+
) : (
|
|
101
|
+
<span>{t('resetPassword', 'Reset Password')}</span>
|
|
102
|
+
)}
|
|
103
|
+
</Button>
|
|
104
|
+
<div style={{ padding: "15px", textAlign: "center" }}>
|
|
105
|
+
<Link to="/login" style={{ textDecoration: "none", cursor: "pointer" }}> Back to Login</Link>
|
|
106
|
+
</div>
|
|
107
|
+
</Form>
|
|
108
|
+
</Tile>
|
|
109
|
+
</div>
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
export default ForgotPassword;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export function initiatePasswordReset(usernameOrEmail: string) {
|
|
4
|
+
return openmrsFetch(`${restBaseUrl}/passwordreset`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7
|
+
body: {
|
|
8
|
+
usernameOrEmail
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
3
|
+
|
|
4
|
+
.submitButton {
|
|
5
|
+
margin-top: layout.$spacing-06;
|
|
6
|
+
width: 18rem;
|
|
7
|
+
|
|
8
|
+
:global(.cds--inline-loading) {
|
|
9
|
+
min-height: layout.$spacing-05;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
:global(.cds--inline-loading__text) {
|
|
13
|
+
font-size: unset;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.alignCenter {
|
|
18
|
+
display: flex;
|
|
19
|
+
text-align: center;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.panelItemContainer {
|
|
23
|
+
a {
|
|
24
|
+
@extend .alignCenter;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
div {
|
|
29
|
+
@extend .alignCenter;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.container {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-wrap: wrap;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
justify-content: center;
|
|
38
|
+
align-items: center;
|
|
39
|
+
position: relative;
|
|
40
|
+
margin-top: layout.$spacing-08;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.changePasswordCard {
|
|
44
|
+
border-radius: 0;
|
|
45
|
+
border: 1px solid $ui-03;
|
|
46
|
+
background-color: $ui-02;
|
|
47
|
+
width: 23rem;
|
|
48
|
+
padding: layout.$spacing-08;
|
|
49
|
+
position: relative;
|
|
50
|
+
min-height: fit-content;
|
|
51
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import React, { useCallback, useState } from "react";
|
|
2
|
+
import { useParams } from "react-router-dom";
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
6
|
+
import { Controller, useForm, type SubmitHandler } from 'react-hook-form';
|
|
7
|
+
import { Button, Form, InlineLoading, Tile, PasswordInput } from '@carbon/react';
|
|
8
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
9
|
+
import { resetPassword } from './reset-password.resource';
|
|
10
|
+
import Logo from '../../logo.component';
|
|
11
|
+
import styles from '../../forgot-password/forgot-password.scss';
|
|
12
|
+
|
|
13
|
+
const ResetPassword: React.FC = () => {
|
|
14
|
+
const { activationKey } = useParams<{ activationKey: string }>();
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
const [isResetingPassword, setIsResetingPassword] = useState(false);
|
|
17
|
+
|
|
18
|
+
const newPasswordValidation = z.string({
|
|
19
|
+
required_error: t('newPasswordRequired', 'New password is required'),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const passwordConfirmationValidation = z.string({
|
|
23
|
+
required_error: t('passwordConfirmationRequired', 'Password confirmation is required'),
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const resetPasswordFormSchema = z
|
|
27
|
+
.object({
|
|
28
|
+
newPassword: newPasswordValidation,
|
|
29
|
+
passwordConfirmation: passwordConfirmationValidation,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const {
|
|
33
|
+
handleSubmit,
|
|
34
|
+
control,
|
|
35
|
+
formState: { errors },
|
|
36
|
+
} = useForm({
|
|
37
|
+
resolver: zodResolver(resetPasswordFormSchema),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const onSubmit: SubmitHandler<z.infer<typeof resetPasswordFormSchema>> = useCallback(
|
|
41
|
+
(data) => {
|
|
42
|
+
setIsResetingPassword(true);
|
|
43
|
+
|
|
44
|
+
const { newPassword } = data;
|
|
45
|
+
|
|
46
|
+
resetPassword(newPassword, activationKey || "")
|
|
47
|
+
.then(() => {
|
|
48
|
+
showSnackbar({
|
|
49
|
+
title: t('passwordResetSuccessfull', 'Password reset successfull'),
|
|
50
|
+
kind: 'success',
|
|
51
|
+
});
|
|
52
|
+
})
|
|
53
|
+
.catch((error) => {
|
|
54
|
+
showSnackbar({
|
|
55
|
+
kind: 'error',
|
|
56
|
+
subtitle: error?.message,
|
|
57
|
+
title: t('errorResetingPassword', 'Error reseting password'),
|
|
58
|
+
});
|
|
59
|
+
})
|
|
60
|
+
.finally(() => {
|
|
61
|
+
setIsResetingPassword(false);
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
[t, activationKey],
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const onError = useCallback(() => setIsResetingPassword(false), []);
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className={styles.container}>
|
|
71
|
+
<Tile className={styles.changePasswordCard}>
|
|
72
|
+
<div className={styles.alignCenter}>
|
|
73
|
+
<Logo t={t} />
|
|
74
|
+
</div>
|
|
75
|
+
<Form onSubmit={handleSubmit(onSubmit, onError)}>
|
|
76
|
+
<Controller
|
|
77
|
+
name="newPassword"
|
|
78
|
+
control={control}
|
|
79
|
+
render={({ field: { onChange, value } }) => (
|
|
80
|
+
<PasswordInput
|
|
81
|
+
id="newPassword"
|
|
82
|
+
invalid={!!errors?.newPassword}
|
|
83
|
+
invalidText={
|
|
84
|
+
(errors &&
|
|
85
|
+
errors.newPassword &&
|
|
86
|
+
errors.newPassword.message &&
|
|
87
|
+
typeof errors.newPassword.message === 'string' &&
|
|
88
|
+
errors.newPassword.message) ??
|
|
89
|
+
''
|
|
90
|
+
}
|
|
91
|
+
labelText={t('newPassword', 'New password')}
|
|
92
|
+
onChange={onChange}
|
|
93
|
+
value={value}
|
|
94
|
+
/>
|
|
95
|
+
)}
|
|
96
|
+
/>
|
|
97
|
+
<Controller
|
|
98
|
+
name="passwordConfirmation"
|
|
99
|
+
control={control}
|
|
100
|
+
render={({ field: { onChange, value } }) => (
|
|
101
|
+
<PasswordInput
|
|
102
|
+
id="passwordConfirmation"
|
|
103
|
+
invalid={!!errors?.passwordConfirmation}
|
|
104
|
+
invalidText={
|
|
105
|
+
(errors &&
|
|
106
|
+
errors.passwordConfirmation &&
|
|
107
|
+
errors.passwordConfirmation.message &&
|
|
108
|
+
typeof errors.passwordConfirmation.message === 'string' &&
|
|
109
|
+
errors.passwordConfirmation.message) ??
|
|
110
|
+
''
|
|
111
|
+
}
|
|
112
|
+
labelText={t('confirmPassword', 'Confirm new password')}
|
|
113
|
+
onChange={onChange}
|
|
114
|
+
value={value}
|
|
115
|
+
/>
|
|
116
|
+
)}
|
|
117
|
+
/>
|
|
118
|
+
<Button className={styles.submitButton} disabled={isResetingPassword} type="submit">
|
|
119
|
+
{isResetingPassword ? (
|
|
120
|
+
<InlineLoading description={t('resettingPassword', 'Resetting password') + '...'} />
|
|
121
|
+
) : (
|
|
122
|
+
<span>{t('resetPassword', 'Reset Password')}</span>
|
|
123
|
+
)}
|
|
124
|
+
</Button>
|
|
125
|
+
</Form>
|
|
126
|
+
</Tile>
|
|
127
|
+
</div>
|
|
128
|
+
)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export default ResetPassword;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export function resetPassword(newPassword: string, activationKey: string) {
|
|
4
|
+
return openmrsFetch(`${restBaseUrl}/passwordreset/${activationKey}`, {
|
|
5
|
+
method: 'POST',
|
|
6
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7
|
+
body: {
|
|
8
|
+
newPassword
|
|
9
|
+
},
|
|
10
|
+
});
|
|
11
|
+
}
|