@ampath/esm-dha-workflow-app 4.0.0-next.4 → 4.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/161.js +1 -0
- package/dist/161.js.map +1 -0
- package/dist/198.js +2 -0
- package/dist/{561.js.LICENSE.txt → 198.js.LICENSE.txt} +10 -0
- package/dist/198.js.map +1 -0
- package/dist/244.js +1 -0
- package/dist/244.js.map +1 -0
- package/dist/274.js +1 -0
- package/dist/274.js.map +1 -0
- package/dist/802.js +2 -0
- package/dist/{731.js.LICENSE.txt → 802.js.LICENSE.txt} +3 -3
- package/dist/802.js.map +1 -0
- package/dist/91.js +1 -1
- package/dist/91.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-home-app.js +1 -1
- package/dist/openmrs-esm-home-app.js.buildmanifest.json +135 -89
- package/dist/openmrs-esm-home-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/index.ts +5 -0
- package/src/registry/client-details/client-details.tsx +40 -0
- package/src/registry/modal/client-details-modal/client-details-modal.scss +28 -0
- package/src/registry/modal/client-details-modal/client-details-modal.tsx +75 -0
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.scss +13 -0
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +176 -0
- package/src/registry/payment-details/payment-options/payment-options.tsx +21 -0
- package/src/registry/registry.component.scss +68 -0
- package/src/registry/registry.component.tsx +269 -2
- package/src/registry/registry.resource.ts +58 -0
- package/src/registry/types/index.ts +160 -0
- package/src/registry/utils/hie-adapter.ts +56 -0
- package/src/registry/utils/mask-data.ts +21 -0
- package/src/root.component.tsx +11 -14
- package/src/routes.json +15 -6
- package/src/side-nav-menu/nav-links.tsx +13 -10
- package/dist/561.js +0 -2
- package/dist/561.js.map +0 -1
- package/dist/70.js +0 -1
- package/dist/70.js.map +0 -1
- package/dist/731.js +0 -2
- package/dist/731.js.map +0 -1
- package/dist/819.js +0 -1
- package/dist/819.js.map +0 -1
|
@@ -1,7 +1,274 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
Button,
|
|
3
|
+
Dropdown,
|
|
4
|
+
InlineLoading,
|
|
5
|
+
RadioButton,
|
|
6
|
+
RadioButtonGroup,
|
|
7
|
+
SideNav,
|
|
8
|
+
Table,
|
|
9
|
+
TableBody,
|
|
10
|
+
TableCell,
|
|
11
|
+
TableHead,
|
|
12
|
+
TableHeader,
|
|
13
|
+
TableRow,
|
|
14
|
+
TextInput,
|
|
15
|
+
} from '@carbon/react';
|
|
16
|
+
import React, { useState } from 'react';
|
|
17
|
+
import styles from './registry.component.scss';
|
|
18
|
+
import { type HieClient, IDENTIFIER_TYPES, type IdentifierType, type RequestCustomOtpDto } from './types';
|
|
19
|
+
import { fetchClientRegistryData } from './registry.resource';
|
|
20
|
+
import { showSnackbar, useLeftNav, useSession } from '@openmrs/esm-framework';
|
|
21
|
+
import OtpVerificationModal from './modal/otp-verification-modal/otp-verification-modal';
|
|
22
|
+
import { maskExceptFirstAndLast, maskValue } from './utils/mask-data';
|
|
23
|
+
import ClientDetailsModal from './modal/client-details-modal/client-details-modal';
|
|
24
|
+
import NavLinks from '../side-nav-menu/nav-links';
|
|
2
25
|
interface RegistryComponentProps {}
|
|
3
26
|
const RegistryComponent: React.FC<RegistryComponentProps> = () => {
|
|
4
|
-
|
|
27
|
+
const [identifierType, setIdentifierType] = useState<IdentifierType>('National ID');
|
|
28
|
+
const [identifierValue, setIdentifierValue] = useState('');
|
|
29
|
+
const [loading, setLoading] = useState<boolean>(false);
|
|
30
|
+
const [client, setClient] = useState<HieClient>();
|
|
31
|
+
const [selectedPatient, setSelectedPatient] = useState<string>('principal');
|
|
32
|
+
const [displayOtpModal, setDisplayOtpModal] = useState<boolean>(false);
|
|
33
|
+
const [displayClientDetailsModal, setDisplayClientDetailsModal] = useState<boolean>(false);
|
|
34
|
+
const [requestCustomOtpDto, setRequestCustomOtpDto] = useState<RequestCustomOtpDto>();
|
|
35
|
+
const session = useSession();
|
|
36
|
+
const locationUuid = session.sessionLocation.uuid;
|
|
37
|
+
|
|
38
|
+
const handleSearchPatient = async () => {
|
|
39
|
+
setLoading(true);
|
|
40
|
+
try {
|
|
41
|
+
const payload = {
|
|
42
|
+
identificationNumber: identifierValue,
|
|
43
|
+
identificationType: identifierType,
|
|
44
|
+
locationUuid,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
if (!isValidCustomSmsPayload(payload)) return false;
|
|
48
|
+
|
|
49
|
+
setRequestCustomOtpDto(payload);
|
|
50
|
+
|
|
51
|
+
const result = await fetchClientRegistryData(payload);
|
|
52
|
+
const patients = Array.isArray(result) ? result : [];
|
|
53
|
+
|
|
54
|
+
if (patients.length === 0) throw new Error('No matching patient found in Client Registry.');
|
|
55
|
+
|
|
56
|
+
const patient = patients[0];
|
|
57
|
+
setClient(patient);
|
|
58
|
+
showAlert('success', 'Client Data Loaded', 'Patient fetched successfully');
|
|
59
|
+
} catch (err: any) {
|
|
60
|
+
const errorMessage = err.message || 'Failed to fetch client data';
|
|
61
|
+
showAlert('error', 'Fetch Failed', errorMessage);
|
|
62
|
+
} finally {
|
|
63
|
+
setLoading(false);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
const isValidCustomSmsPayload = (payload: RequestCustomOtpDto): boolean => {
|
|
67
|
+
if (!payload.identificationNumber) {
|
|
68
|
+
showAlert('error', 'Please enter a valid identification number', '');
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
if (!payload.identificationType) {
|
|
72
|
+
showAlert('error', 'Please enter a valid identification type', '');
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (!payload.locationUuid) {
|
|
76
|
+
showAlert('error', 'No default location selected', '');
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
};
|
|
81
|
+
const showAlert = (alertType: 'error' | 'success', title: string, subtitle: string) => {
|
|
82
|
+
showSnackbar({
|
|
83
|
+
kind: alertType,
|
|
84
|
+
title: title,
|
|
85
|
+
subtitle: subtitle,
|
|
86
|
+
});
|
|
87
|
+
};
|
|
88
|
+
const handleSelectedPatient = (sp: string) => {
|
|
89
|
+
setSelectedPatient(sp);
|
|
90
|
+
};
|
|
91
|
+
const handleOtpVerification = () => {
|
|
92
|
+
setDisplayOtpModal(true);
|
|
93
|
+
};
|
|
94
|
+
const handleModelClose = () => {
|
|
95
|
+
setDisplayOtpModal(false);
|
|
96
|
+
setDisplayClientDetailsModal(true);
|
|
97
|
+
};
|
|
98
|
+
const onClientDetailsModalClose = () => {
|
|
99
|
+
setDisplayClientDetailsModal(false);
|
|
100
|
+
};
|
|
101
|
+
const handleClientDetailsSubmit = () => {
|
|
102
|
+
return;
|
|
103
|
+
};
|
|
104
|
+
return (
|
|
105
|
+
/*
|
|
106
|
+
To be refactored ton use gloab-nav-slot
|
|
107
|
+
*/
|
|
108
|
+
<>
|
|
109
|
+
<div className={styles.registryLayout}>
|
|
110
|
+
<div className={styles.sideNavLayout}>
|
|
111
|
+
<SideNav isFixedNav expanded={true} aria-label="Side navigation">
|
|
112
|
+
<NavLinks />
|
|
113
|
+
</SideNav>
|
|
114
|
+
</div>
|
|
115
|
+
<div className={styles.mainContent}>
|
|
116
|
+
<div className={styles.registryHeader}>
|
|
117
|
+
<h4>Client Registry</h4>
|
|
118
|
+
<p>Please enter identification number to begin</p>
|
|
119
|
+
</div>
|
|
120
|
+
<div className={styles.registryContent}>
|
|
121
|
+
<div className={styles.formRow}>
|
|
122
|
+
<div className={styles.formControl}>
|
|
123
|
+
<Dropdown
|
|
124
|
+
id="identifier-type-dropdown"
|
|
125
|
+
label="Identifier Type"
|
|
126
|
+
titleText="Select Identifier Type"
|
|
127
|
+
items={IDENTIFIER_TYPES}
|
|
128
|
+
selectedItem={identifierType}
|
|
129
|
+
onChange={({ selectedItem }) => setIdentifierType(selectedItem as IdentifierType)}
|
|
130
|
+
/>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<div className={styles.formControl}>
|
|
134
|
+
<TextInput
|
|
135
|
+
id="identifier-value"
|
|
136
|
+
labelText={`${identifierType} Value`}
|
|
137
|
+
value={identifierValue}
|
|
138
|
+
onChange={(e) => setIdentifierValue(e.target.value)}
|
|
139
|
+
placeholder={`Enter ${identifierType.toLowerCase()} value`}
|
|
140
|
+
/>
|
|
141
|
+
</div>
|
|
142
|
+
<div className={styles.formControl}>
|
|
143
|
+
<Button kind="primary" onClick={handleSearchPatient} disabled={loading}>
|
|
144
|
+
{loading ? <InlineLoading description="Searching..." /> : 'Search'}
|
|
145
|
+
</Button>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
{client ? (
|
|
149
|
+
<div className={styles.formRow}>
|
|
150
|
+
<div className={styles.hieData}>
|
|
151
|
+
<div className={styles.selectionHeader}>
|
|
152
|
+
<h5>Please select one patient and request patient to share the OTP sent</h5>
|
|
153
|
+
</div>
|
|
154
|
+
<div className={styles.patientSelect}>
|
|
155
|
+
<div className={styles.patientSelectRadio}>
|
|
156
|
+
<RadioButtonGroup
|
|
157
|
+
defaultSelected="principal"
|
|
158
|
+
legendText="Patient"
|
|
159
|
+
onChange={(v) => handleSelectedPatient(v as string)}
|
|
160
|
+
name="radio-button-default-group"
|
|
161
|
+
>
|
|
162
|
+
<RadioButton id="principal" labelText="Principal" value="principal" />
|
|
163
|
+
<RadioButton id="dependants" labelText="Dependants" value="dependants" />
|
|
164
|
+
</RadioButtonGroup>
|
|
165
|
+
</div>
|
|
166
|
+
<div className={styles.patientConfirmSelection}>
|
|
167
|
+
<div className={styles.btnContainer}>
|
|
168
|
+
<Button kind="primary" onClick={handleOtpVerification}>
|
|
169
|
+
{' '}
|
|
170
|
+
Confirm
|
|
171
|
+
</Button>
|
|
172
|
+
</div>
|
|
173
|
+
<div className={styles.btnContainer}>
|
|
174
|
+
<Button kind="secondary">Cancel</Button>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
{selectedPatient === 'principal' ? (
|
|
179
|
+
<>
|
|
180
|
+
<Table>
|
|
181
|
+
<TableHead>
|
|
182
|
+
<TableRow>
|
|
183
|
+
<TableHeader>Name</TableHeader>
|
|
184
|
+
<TableHeader>CR</TableHeader>
|
|
185
|
+
<TableHeader>Phone No</TableHeader>
|
|
186
|
+
<TableHeader>ID No</TableHeader>
|
|
187
|
+
</TableRow>
|
|
188
|
+
</TableHead>
|
|
189
|
+
<TableBody>
|
|
190
|
+
<TableRow>
|
|
191
|
+
<TableCell>
|
|
192
|
+
{client.first_name} {maskExceptFirstAndLast(client.middle_name)}{' '}
|
|
193
|
+
{maskExceptFirstAndLast(client.last_name)}
|
|
194
|
+
</TableCell>
|
|
195
|
+
<TableCell>{maskValue(client.id)}</TableCell>
|
|
196
|
+
<TableCell>{maskValue(client.phone)}</TableCell>
|
|
197
|
+
<TableCell>{maskValue(client.identification_number)}</TableCell>
|
|
198
|
+
</TableRow>
|
|
199
|
+
</TableBody>
|
|
200
|
+
</Table>
|
|
201
|
+
</>
|
|
202
|
+
) : (
|
|
203
|
+
<></>
|
|
204
|
+
)}
|
|
205
|
+
{selectedPatient === 'dependants' ? (
|
|
206
|
+
<>
|
|
207
|
+
<Table>
|
|
208
|
+
<TableHead>
|
|
209
|
+
<TableRow>
|
|
210
|
+
<TableHeader>Name</TableHeader>
|
|
211
|
+
<TableHeader>CR</TableHeader>
|
|
212
|
+
<TableHeader>Relationship</TableHeader>
|
|
213
|
+
</TableRow>
|
|
214
|
+
</TableHead>
|
|
215
|
+
<TableBody>
|
|
216
|
+
{client.dependants.map((d) => {
|
|
217
|
+
const dependant = d.result[0];
|
|
218
|
+
const relationship = d.relationship;
|
|
219
|
+
return (
|
|
220
|
+
<>
|
|
221
|
+
<TableRow>
|
|
222
|
+
<TableCell>
|
|
223
|
+
{dependant.first_name} {maskExceptFirstAndLast(dependant.middle_name)}{' '}
|
|
224
|
+
{maskExceptFirstAndLast(dependant.last_name)}
|
|
225
|
+
</TableCell>
|
|
226
|
+
<TableCell>{maskValue(dependant.id)}</TableCell>
|
|
227
|
+
<TableCell>{relationship}</TableCell>
|
|
228
|
+
</TableRow>
|
|
229
|
+
</>
|
|
230
|
+
);
|
|
231
|
+
})}
|
|
232
|
+
</TableBody>
|
|
233
|
+
</Table>
|
|
234
|
+
</>
|
|
235
|
+
) : (
|
|
236
|
+
<></>
|
|
237
|
+
)}
|
|
238
|
+
|
|
239
|
+
{displayOtpModal ? (
|
|
240
|
+
<OtpVerificationModal
|
|
241
|
+
requestCustomOtpDto={requestCustomOtpDto}
|
|
242
|
+
phoneNumber={client.phone}
|
|
243
|
+
open={displayOtpModal}
|
|
244
|
+
onModalClose={handleModelClose}
|
|
245
|
+
/>
|
|
246
|
+
) : (
|
|
247
|
+
<></>
|
|
248
|
+
)}
|
|
249
|
+
|
|
250
|
+
{client && displayClientDetailsModal ? (
|
|
251
|
+
<>
|
|
252
|
+
<ClientDetailsModal
|
|
253
|
+
client={client}
|
|
254
|
+
open={displayClientDetailsModal}
|
|
255
|
+
onModalClose={onClientDetailsModalClose}
|
|
256
|
+
onSubmit={handleClientDetailsSubmit}
|
|
257
|
+
/>{' '}
|
|
258
|
+
</>
|
|
259
|
+
) : (
|
|
260
|
+
<></>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
) : (
|
|
265
|
+
<></>
|
|
266
|
+
)}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
</>
|
|
271
|
+
);
|
|
5
272
|
};
|
|
6
273
|
|
|
7
274
|
export default RegistryComponent;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ClientRegistrySearchRequest,
|
|
3
|
+
type RequestCustomOtpDto,
|
|
4
|
+
type RequestCustomOtpResponse,
|
|
5
|
+
type ValidateCustomOtpResponse,
|
|
6
|
+
type ValidateHieCustomOtpDto,
|
|
7
|
+
} from './types';
|
|
8
|
+
|
|
9
|
+
const HIE_BASE_URL = 'https://staging.ampath.or.ke/hie';
|
|
10
|
+
|
|
11
|
+
export type ClientRegistrySearchResponse = any[];
|
|
12
|
+
|
|
13
|
+
async function postJson<T>(url: string, payload: unknown): Promise<T> {
|
|
14
|
+
const response = await fetch(url, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify(payload),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
const errorText = await response.text();
|
|
22
|
+
throw new Error(`Request failed with ${response.status}: ${errorText}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return response.json() as Promise<T>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function requestCustomOtp(payload: RequestCustomOtpDto): Promise<RequestCustomOtpResponse> {
|
|
29
|
+
const url = `${HIE_BASE_URL}/client/send-custom-otp`;
|
|
30
|
+
const formattedPayload = {
|
|
31
|
+
identificationNumber: payload.identificationNumber,
|
|
32
|
+
identificationType: payload.identificationType,
|
|
33
|
+
locationUuid: payload.locationUuid,
|
|
34
|
+
};
|
|
35
|
+
return postJson<RequestCustomOtpResponse>(url, formattedPayload);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function validateCustomOtp(payload: ValidateHieCustomOtpDto): Promise<ValidateCustomOtpResponse> {
|
|
39
|
+
const url = `${HIE_BASE_URL}/client/validate-custom-otp`;
|
|
40
|
+
const formattedPayload = {
|
|
41
|
+
sessionId: payload.sessionId,
|
|
42
|
+
otp: payload.otp,
|
|
43
|
+
locationUuid: payload.locationUuid,
|
|
44
|
+
};
|
|
45
|
+
return postJson<ValidateCustomOtpResponse>(url, formattedPayload);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function fetchClientRegistryData(
|
|
49
|
+
payload: ClientRegistrySearchRequest,
|
|
50
|
+
): Promise<ClientRegistrySearchResponse> {
|
|
51
|
+
const url = `${HIE_BASE_URL}/client/search`;
|
|
52
|
+
const formattedPayload = {
|
|
53
|
+
identificationNumber: payload.identificationNumber,
|
|
54
|
+
identificationType: payload.identificationType,
|
|
55
|
+
locationUuid: payload.locationUuid,
|
|
56
|
+
};
|
|
57
|
+
return postJson<ClientRegistrySearchResponse>(url, formattedPayload);
|
|
58
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
export type IdentifierType = 'National ID' | 'Alien ID' | 'Passport' | 'Mandate Number' | 'Refugee ID';
|
|
2
|
+
|
|
3
|
+
export const IDENTIFIER_TYPES: IdentifierType[] = [
|
|
4
|
+
'National ID',
|
|
5
|
+
'Alien ID',
|
|
6
|
+
'Passport',
|
|
7
|
+
'Mandate Number',
|
|
8
|
+
'Refugee ID',
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
export interface ValidateCustomOtpResponse {
|
|
12
|
+
message: string;
|
|
13
|
+
isValid?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ClientRegistrySearchRequest = {
|
|
17
|
+
identificationNumber: string | number;
|
|
18
|
+
identificationType: string;
|
|
19
|
+
locationUuid: string;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface RequestCustomOtpResponse {
|
|
23
|
+
message: string;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
maskedPhone: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ValidateHieCustomOtpDto {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
otp: number | string;
|
|
31
|
+
locationUuid: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ValidateCustomOtpResponse {
|
|
35
|
+
message: string;
|
|
36
|
+
isValid?: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export type RequestCustomOtpDto = {
|
|
40
|
+
identificationNumber: string | number;
|
|
41
|
+
identificationType: string;
|
|
42
|
+
locationUuid: string;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export interface RequestCustomOtpResponse {
|
|
46
|
+
message: string;
|
|
47
|
+
sessionId: string;
|
|
48
|
+
maskedPhone: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ValidateHieCustomOtpDto {
|
|
52
|
+
sessionId: string;
|
|
53
|
+
otp: number | string;
|
|
54
|
+
locationUuid: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export enum HieIdentificationType {
|
|
58
|
+
NationalID = 'National ID',
|
|
59
|
+
SHANumber = 'SHA Number',
|
|
60
|
+
HouseholdNumber = 'Household Number',
|
|
61
|
+
RefugeeID = 'Refugee ID',
|
|
62
|
+
AlienID = 'Alien ID',
|
|
63
|
+
MandateNumber = 'Mandate Number',
|
|
64
|
+
Cr = 'id',
|
|
65
|
+
TemporaryDependantID = 'Temporary Dependant ID',
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface HieIdentifications {
|
|
69
|
+
identification_number: string;
|
|
70
|
+
identification_type: HieIdentificationType;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface HieDependant {
|
|
74
|
+
date_added: string;
|
|
75
|
+
relationship: string;
|
|
76
|
+
total: number;
|
|
77
|
+
result: HieClient[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface AlternateContact {
|
|
81
|
+
contact_type: string;
|
|
82
|
+
contact_id: string;
|
|
83
|
+
contact_name: string;
|
|
84
|
+
relationship: string;
|
|
85
|
+
remarks: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface HieClient {
|
|
89
|
+
resourceType: string;
|
|
90
|
+
id: string;
|
|
91
|
+
meta: {
|
|
92
|
+
versionId: string;
|
|
93
|
+
creationTime: string;
|
|
94
|
+
lastUpdated: string;
|
|
95
|
+
source: string;
|
|
96
|
+
};
|
|
97
|
+
originSystem: {
|
|
98
|
+
system: string;
|
|
99
|
+
record_id: string;
|
|
100
|
+
};
|
|
101
|
+
title: string;
|
|
102
|
+
first_name: string;
|
|
103
|
+
middle_name: string;
|
|
104
|
+
last_name: string;
|
|
105
|
+
gender: string;
|
|
106
|
+
date_of_birth: string;
|
|
107
|
+
place_of_birth: string;
|
|
108
|
+
person_with_disability: number;
|
|
109
|
+
citizenship: string;
|
|
110
|
+
kra_pin: string;
|
|
111
|
+
preferred_primary_care_network: string;
|
|
112
|
+
employment_type: string;
|
|
113
|
+
domestic_worker_type: string;
|
|
114
|
+
civil_status: string;
|
|
115
|
+
identification_type: HieIdentificationType;
|
|
116
|
+
identification_number: string;
|
|
117
|
+
other_identifications: HieIdentifications[];
|
|
118
|
+
dependants: HieDependant[];
|
|
119
|
+
is_alive: number;
|
|
120
|
+
deceased_datetime: string;
|
|
121
|
+
phone: string;
|
|
122
|
+
biometrics_verified: number;
|
|
123
|
+
biometrics_score: number;
|
|
124
|
+
email: string;
|
|
125
|
+
country: string;
|
|
126
|
+
county: string;
|
|
127
|
+
sub_county: string;
|
|
128
|
+
ward: string;
|
|
129
|
+
village_estate: string;
|
|
130
|
+
building_house_no: string;
|
|
131
|
+
latitude: string;
|
|
132
|
+
longitude: string;
|
|
133
|
+
province_state_country: string;
|
|
134
|
+
zip_code: string;
|
|
135
|
+
identification_residence: string;
|
|
136
|
+
employer_name: string;
|
|
137
|
+
employer_pin: string;
|
|
138
|
+
disability_category: string;
|
|
139
|
+
disability_subcategory: string;
|
|
140
|
+
disability_cause: string;
|
|
141
|
+
in_lawful_custody: string;
|
|
142
|
+
admission_remand_number: string;
|
|
143
|
+
document_uploads: any[];
|
|
144
|
+
alternative_contacts: AlternateContact[];
|
|
145
|
+
gross_income: number;
|
|
146
|
+
gross_income_currency: string;
|
|
147
|
+
postal_address: string;
|
|
148
|
+
estimated_contribution: number;
|
|
149
|
+
estimated_annual_contribution: number;
|
|
150
|
+
city: string;
|
|
151
|
+
id_serial: string;
|
|
152
|
+
learning_institution_code: string;
|
|
153
|
+
learning_institution_name: string;
|
|
154
|
+
grade_level: string;
|
|
155
|
+
admission_number: string;
|
|
156
|
+
expected_year_of_graduation: string;
|
|
157
|
+
unconfirmed_dependants: HieDependant[];
|
|
158
|
+
is_agent: number;
|
|
159
|
+
agent_id: string;
|
|
160
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type HieIdentifications, type HieClient } from '../types';
|
|
2
|
+
|
|
3
|
+
const clientDatailsFields = [
|
|
4
|
+
'id',
|
|
5
|
+
'other_identifications',
|
|
6
|
+
'first_name',
|
|
7
|
+
'middle_name',
|
|
8
|
+
'last_name',
|
|
9
|
+
'gender',
|
|
10
|
+
'date_of_birth',
|
|
11
|
+
'is_alive',
|
|
12
|
+
'deceased_datetime',
|
|
13
|
+
'phone',
|
|
14
|
+
'email',
|
|
15
|
+
'civil_status',
|
|
16
|
+
'place_of_birth',
|
|
17
|
+
'citizenship',
|
|
18
|
+
'country',
|
|
19
|
+
'county',
|
|
20
|
+
'sub_county',
|
|
21
|
+
'ward',
|
|
22
|
+
'village_estate',
|
|
23
|
+
'longitude',
|
|
24
|
+
'latitude',
|
|
25
|
+
'identification_type',
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
export function generateHieClientDetails(hieClient: HieClient) {
|
|
29
|
+
let data = {};
|
|
30
|
+
Object.keys(hieClient)
|
|
31
|
+
.filter((key) => {
|
|
32
|
+
return clientDatailsFields.includes(key);
|
|
33
|
+
})
|
|
34
|
+
.forEach((key) => {
|
|
35
|
+
if (key === 'other_identifications') {
|
|
36
|
+
const otherIds = generateOtherIdentifications(hieClient[key]);
|
|
37
|
+
data = {
|
|
38
|
+
...data,
|
|
39
|
+
...otherIds,
|
|
40
|
+
};
|
|
41
|
+
} else if (key === 'identification_type') {
|
|
42
|
+
data[hieClient['identification_type']] = hieClient.identification_number;
|
|
43
|
+
} else {
|
|
44
|
+
const value = hieClient[key];
|
|
45
|
+
data[key] = value;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
return data;
|
|
49
|
+
}
|
|
50
|
+
function generateOtherIdentifications(hieIdentifications: HieIdentifications[]) {
|
|
51
|
+
const other = {};
|
|
52
|
+
hieIdentifications.forEach((id) => {
|
|
53
|
+
other[id.identification_type] = id.identification_number;
|
|
54
|
+
});
|
|
55
|
+
return other;
|
|
56
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const maskValue = (value: string): string => {
|
|
2
|
+
let arrValue = value.split('');
|
|
3
|
+
for (let i = 0; i < value.length; i++) {
|
|
4
|
+
if (i % 2 === 0) {
|
|
5
|
+
arrValue[i] = '*';
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const maskedValue = arrValue.join('');
|
|
9
|
+
return maskedValue;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const maskExceptFirstAndLast = (value: string): string => {
|
|
13
|
+
let arrValue = value.split('');
|
|
14
|
+
for (let i = 0; i < value.length; i++) {
|
|
15
|
+
if (!(i == 0 || i === value.length - 1)) {
|
|
16
|
+
arrValue[i] = '*';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const maskedValue = arrValue.join('');
|
|
20
|
+
return maskedValue;
|
|
21
|
+
};
|
package/src/root.component.tsx
CHANGED
|
@@ -15,24 +15,21 @@ import Greeter from './greeter/greeter.component';
|
|
|
15
15
|
import PatientGetter from './patient-getter/patient-getter.component';
|
|
16
16
|
import Resources from './resources/resources.component';
|
|
17
17
|
import styles from './root.scss';
|
|
18
|
+
import { useConfig, useLeftNav } from '@openmrs/esm-framework';
|
|
19
|
+
import { BrowserRouter, Route, Routes } from 'react-router-dom';
|
|
20
|
+
import RegistryComponent from './registry/registry.component';
|
|
18
21
|
|
|
19
22
|
const Root: React.FC = () => {
|
|
20
23
|
const { t } = useTranslation();
|
|
21
|
-
|
|
22
24
|
return (
|
|
23
|
-
<
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<Boxes />
|
|
32
|
-
{/* PatientGetter: demonstrates data fetching */}
|
|
33
|
-
<PatientGetter />
|
|
34
|
-
<Resources />
|
|
35
|
-
</div>
|
|
25
|
+
<main className="omrs-main-content">
|
|
26
|
+
<BrowserRouter basename={window.spaBase}>
|
|
27
|
+
<Routes>
|
|
28
|
+
<Route path="/workflow" element={<RegistryComponent />} />
|
|
29
|
+
<Route path="/workflow/registry" element={<RegistryComponent />} />
|
|
30
|
+
</Routes>
|
|
31
|
+
</BrowserRouter>
|
|
32
|
+
</main>
|
|
36
33
|
);
|
|
37
34
|
};
|
|
38
35
|
|
package/src/routes.json
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
3
|
"backendDependencies": {},
|
|
4
|
-
|
|
4
|
+
"pages": [
|
|
5
|
+
{
|
|
6
|
+
"component": "root",
|
|
7
|
+
"route": "workflow",
|
|
8
|
+
"online": true,
|
|
9
|
+
"offline": true
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"extensions": [
|
|
5
13
|
{
|
|
6
14
|
"component": "workflowRegistryLink",
|
|
7
15
|
"name": "workflow-registry-link",
|
|
8
16
|
"slot": "app-menu-slot",
|
|
9
17
|
"online": true,
|
|
10
18
|
"offline": true
|
|
11
|
-
}
|
|
12
|
-
],
|
|
13
|
-
"pages": [
|
|
19
|
+
},
|
|
14
20
|
{
|
|
15
|
-
"component": "
|
|
16
|
-
"
|
|
21
|
+
"component": "navLinks",
|
|
22
|
+
"name": "side-nav-workflow-link",
|
|
23
|
+
"slot": "global-nav-menu-slot",
|
|
24
|
+
"online": true,
|
|
25
|
+
"offline": true
|
|
17
26
|
}
|
|
18
27
|
]
|
|
19
28
|
}
|
|
@@ -5,16 +5,19 @@ interface NavLinksProps {}
|
|
|
5
5
|
const NavLinks: React.FC<NavLinksProps> = () => {
|
|
6
6
|
return (
|
|
7
7
|
<>
|
|
8
|
-
<SideNavMenuItem href="
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
<SideNavMenuItem href="
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
<SideNavMenuItem href="
|
|
15
|
-
<SideNavMenuItem href="
|
|
16
|
-
<SideNavMenuItem href="
|
|
17
|
-
<SideNavMenuItem href="
|
|
8
|
+
<SideNavMenuItem id="1" href="workflow/registry">
|
|
9
|
+
Dashboard
|
|
10
|
+
</SideNavMenuItem>
|
|
11
|
+
<SideNavMenuItem id="2" href="workflow/registry">
|
|
12
|
+
Registration
|
|
13
|
+
</SideNavMenuItem>
|
|
14
|
+
<SideNavMenuItem href="workflow/registry">Appointments</SideNavMenuItem>
|
|
15
|
+
<SideNavMenuItem href="workflow/registry">Triage</SideNavMenuItem>
|
|
16
|
+
<SideNavMenuItem href="workflow/registry">workflow</SideNavMenuItem>
|
|
17
|
+
<SideNavMenuItem href="workflow/registry">Laboratory</SideNavMenuItem>
|
|
18
|
+
<SideNavMenuItem href="workflow/registry">Bookings</SideNavMenuItem>
|
|
19
|
+
<SideNavMenuItem href="workflow/registry">Reports</SideNavMenuItem>
|
|
20
|
+
<SideNavMenuItem href="workflow/registry">Registers</SideNavMenuItem>
|
|
18
21
|
</>
|
|
19
22
|
);
|
|
20
23
|
};
|