@kenyaemr/esm-patient-registration-app 8.0.1-pre.95 → 8.1.0

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.
Files changed (49) hide show
  1. package/.turbo/turbo-build.log +18 -16
  2. package/dist/108.js +1 -0
  3. package/dist/108.js.map +1 -0
  4. package/dist/574.js +1 -1
  5. package/dist/623.js +1 -1
  6. package/dist/735.js +1 -1
  7. package/dist/745.js +2 -0
  8. package/dist/{831.js.LICENSE.txt → 745.js.LICENSE.txt} +10 -0
  9. package/dist/745.js.map +1 -0
  10. package/dist/{59.js → 76.js} +1 -1
  11. package/dist/{59.js.map → 76.js.map} +1 -1
  12. package/dist/830.js +1 -0
  13. package/dist/830.js.map +1 -0
  14. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  15. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +69 -69
  16. package/dist/main.js +1 -1
  17. package/dist/main.js.LICENSE.txt +10 -0
  18. package/dist/main.js.map +1 -1
  19. package/dist/routes.json +1 -1
  20. package/package.json +2 -3
  21. package/src/client-registry/client-registry.component.tsx +22 -0
  22. package/src/client-registry/hie-client-registry/hie-client-registry.component.tsx +134 -0
  23. package/src/client-registry/hie-client-registry/hie-client-registry.scss +53 -0
  24. package/src/client-registry/hie-client-registry/hie-resource.ts +160 -0
  25. package/src/client-registry/hie-client-registry/hie-types.ts +29 -0
  26. package/src/client-registry/hie-client-registry/modal/confirm-hie.modal.tsx +82 -0
  27. package/src/client-registry/hie-client-registry/modal/confirm-hie.scss +10 -0
  28. package/src/{patient-verification → client-registry/patient-verification}/patient-verification-hook.tsx +2 -2
  29. package/src/{patient-verification → client-registry/patient-verification}/patient-verification-utils.ts +1 -1
  30. package/src/{patient-verification → client-registry/patient-verification}/patient-verification.component.tsx +4 -1
  31. package/src/{patient-verification → client-registry/patient-verification}/patient-verification.scss +17 -1
  32. package/src/{patient-verification → client-registry/patient-verification}/verification-modal/empty-prompt.component.tsx +9 -6
  33. package/src/config-schema.ts +37 -0
  34. package/src/index.ts +5 -2
  35. package/src/patient-registration/field/obs/obs-field.component.tsx +1 -1
  36. package/src/patient-registration/patient-registration-hooks.ts +4 -1
  37. package/src/patient-registration/patient-registration.component.tsx +24 -17
  38. package/src/routes.json +7 -2
  39. package/translations/en.json +12 -0
  40. package/dist/330.js +0 -1
  41. package/dist/330.js.map +0 -1
  42. package/dist/564.js +0 -1
  43. package/dist/564.js.map +0 -1
  44. package/dist/831.js +0 -2
  45. package/dist/831.js.map +0 -1
  46. /package/src/{patient-verification → client-registry/patient-verification}/assets/counties.json +0 -0
  47. /package/src/{patient-verification → client-registry/patient-verification}/assets/verification-assets.ts +0 -0
  48. /package/src/{patient-verification → client-registry/patient-verification}/verification-modal/confirm-prompt.component.tsx +0 -0
  49. /package/src/{patient-verification → client-registry/patient-verification}/verification-types.ts +0 -0
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"cancelPatientEditModal","name":"cancel-patient-edit-modal","online":true,"offline":true},{"component":"patientPhotoExtension","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true},{"component":"deleteIdentifierConfirmationModal","name":"delete-identifier-confirmation-modal","online":true,"offline":true},{"component":"emptyClientRegistryModal","name":"empty-client-registry-modal","online":true,"offline":true},{"component":"confirmClientRegistryModal","name":"confirm-client-registry-modal","online":true,"offline":true}],"version":"8.0.1-pre.95"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"cancelPatientEditModal","name":"cancel-patient-edit-modal","online":true,"offline":true},{"component":"patientPhotoExtension","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true},{"component":"deleteIdentifierConfirmationModal","name":"delete-identifier-confirmation-modal","online":true,"offline":true},{"component":"emptyClientRegistryModal","name":"empty-client-registry-modal","online":true,"offline":true},{"component":"confirmClientRegistryModal","name":"confirm-client-registry-modal","online":true,"offline":true}],"modals":[{"component":"hieConfirmationModal","name":"hie-confirmation-modal"}],"version":"8.1.0"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-patient-registration-app",
3
- "version": "8.0.1-pre.95",
3
+ "version": "8.1.0",
4
4
  "description": "Patient registration microfrontend for the OpenMRS SPA",
5
5
  "browser": "dist/kenyaemr-esm-patient-registration-app.js",
6
6
  "main": "src/index.ts",
@@ -53,6 +53,5 @@
53
53
  },
54
54
  "devDependencies": {
55
55
  "webpack": "^5.74.0"
56
- },
57
- "stableVersion": "8.0.0"
56
+ }
58
57
  }
@@ -0,0 +1,22 @@
1
+ import { type FormikProps } from 'formik';
2
+ import React, { type Dispatch } from 'react';
3
+ import { type FormValues } from '../patient-registration/patient-registration.types';
4
+ import { useFeatureFlag } from '@openmrs/esm-framework';
5
+ import PatientVerification from './patient-verification/patient-verification.component';
6
+ import HIEClientRegistry from './hie-client-registry/hie-client-registry.component';
7
+
8
+ type ClientRegistryProps = {
9
+ props: FormikProps<FormValues>;
10
+ setInitialFormValues: Dispatch<FormValues>;
11
+ };
12
+
13
+ const ClientRegistry: React.FC<ClientRegistryProps> = (clientRegistryProps) => {
14
+ const healthInformationExchangeFlag = useFeatureFlag('healthInformationExchange');
15
+
16
+ if (healthInformationExchangeFlag) {
17
+ return <HIEClientRegistry {...clientRegistryProps} />;
18
+ }
19
+ return <PatientVerification {...clientRegistryProps} />;
20
+ };
21
+
22
+ export default ClientRegistry;
@@ -0,0 +1,134 @@
1
+ import React, { type Dispatch } from 'react';
2
+ import styles from './hie-client-registry.scss';
3
+ import { type FormikProps } from 'formik';
4
+ import { type FormValues } from '../../patient-registration/patient-registration.types';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Tile, ComboBox, Button, TextInput, InlineLoading } from '@carbon/react';
7
+ import { Search } from '@carbon/react/icons';
8
+ import { showModal, showSnackbar, useConfig } from '@openmrs/esm-framework';
9
+ import { z } from 'zod';
10
+ import { type RegistrationConfig } from '../../config-schema';
11
+ import { useForm, Controller, type SubmitHandler } from 'react-hook-form';
12
+ import { zodResolver } from '@hookform/resolvers/zod';
13
+ import { fetchPatientFromHIE, mapHIEPatientToFormValues } from './hie-resource';
14
+ import { type HIEPatient } from './hie-types';
15
+
16
+ type HIEClientRegistryProps = {
17
+ props: FormikProps<FormValues>;
18
+ setInitialFormValues: Dispatch<FormValues>;
19
+ };
20
+
21
+ type HIEFormValues = {
22
+ identifierType: string;
23
+ identifierValue: string;
24
+ };
25
+
26
+ const HIEFormSchema = z.object({
27
+ identifierType: z.string().min(1, { message: 'Identifier type is required' }),
28
+ identifierValue: z.string().min(1, { message: 'Identifier value is required' }),
29
+ });
30
+
31
+ const HIEClientRegistry: React.FC<HIEClientRegistryProps> = ({ setInitialFormValues, props }) => {
32
+ const { t } = useTranslation();
33
+ const { control, handleSubmit, watch, formState } = useForm<HIEFormValues>({
34
+ mode: 'all',
35
+ defaultValues: { identifierType: '', identifierValue: '' },
36
+ resolver: zodResolver(HIEFormSchema),
37
+ });
38
+ const {
39
+ hieClientRegistry: { identifierTypes },
40
+ } = useConfig<RegistrationConfig>();
41
+
42
+ const onSubmit: SubmitHandler<HIEFormValues> = async (data: HIEFormValues, event: React.BaseSyntheticEvent) => {
43
+ try {
44
+ const hieClientRegistry = await fetchPatientFromHIE(data.identifierType, data.identifierValue);
45
+
46
+ if (hieClientRegistry && hieClientRegistry.resourceType === 'Patient') {
47
+ const dispose = showModal('hie-confirmation-modal', {
48
+ patient: hieClientRegistry,
49
+ closeModal: () => dispose(),
50
+ onUseValues: () =>
51
+ setInitialFormValues(mapHIEPatientToFormValues(hieClientRegistry as HIEPatient, props.values)),
52
+ });
53
+ }
54
+
55
+ if (hieClientRegistry && hieClientRegistry.resourceType === 'OperationOutcome') {
56
+ const issueMessage = hieClientRegistry?.['issue']?.map((issue) => issue.diagnostics).join(', ');
57
+ const dispose = showModal('empty-client-registry-modal', {
58
+ onConfirm: () => dispose(),
59
+ close: () => dispose(),
60
+ title: t('clientRegistryEmpty', 'Create & Post Patient'),
61
+ message: issueMessage,
62
+ });
63
+ }
64
+ } catch (error) {
65
+ showSnackbar({
66
+ title: t('errorFetchingPatient', 'Error fetching patient'),
67
+ subtitle: error.message,
68
+ kind: 'error',
69
+ isLowContrast: true,
70
+ });
71
+ }
72
+ };
73
+
74
+ return (
75
+ <form onSubmit={handleSubmit(onSubmit)} className={styles.hieContainer}>
76
+ <h3 className={styles.productiveHeading02} style={{ color: '#161616' }}>
77
+ {t('patientVerificationFromHIE', 'Patient verification from HIE')}
78
+ </h3>
79
+ <span className={styles.label01}>
80
+ {t('allFieldsRequiredText', 'All fields are required unless marked optional')}
81
+ </span>
82
+ <Tile className={styles.grid}>
83
+ <Controller
84
+ control={control}
85
+ name="identifierType"
86
+ render={({ field: { onChange, value, onBlur, ref }, fieldState }) => (
87
+ <ComboBox
88
+ light
89
+ style={{ borderBottom: 'none' }}
90
+ onChange={({ selectedItem }) => onChange(selectedItem?.identifierValue)}
91
+ id="identifier-combobox"
92
+ items={identifierTypes}
93
+ placeholder={t('selectIdentifierType', 'Select identifier type')}
94
+ itemToString={(item) => (item ? item.identifierType : '')}
95
+ titleText={t('identifierType', 'Identifier type')}
96
+ invalid={!!fieldState?.error?.message}
97
+ invalidText={fieldState?.error?.message}
98
+ />
99
+ )}
100
+ />
101
+ <Controller
102
+ control={control}
103
+ name="identifierValue"
104
+ render={({ field, fieldState }) => (
105
+ <TextInput
106
+ disabled={watch('identifierType') === ''}
107
+ light
108
+ id="identifier-search"
109
+ placeholder={t('enterIdentifierSearchValue', 'Enter identifier search value')}
110
+ type="text"
111
+ labelText={t('identifierSearch', 'Identifier search')}
112
+ invalid={!!fieldState?.error?.message}
113
+ invalidText={fieldState?.error?.message}
114
+ {...field}
115
+ />
116
+ )}
117
+ />
118
+ <Button
119
+ onClick={handleSubmit(onSubmit)}
120
+ renderIcon={(props) => <Search size={24} {...props} />}
121
+ iconDescription={t('searchRegistry', 'Search registry')}
122
+ size="md"
123
+ disabled={!watch('identifierType') || !watch('identifierValue') || formState.isSubmitting}
124
+ kind="tertiary">
125
+ {formState.isSubmitting
126
+ ? t('searchingRegistry', 'Searching registry...')
127
+ : t('searchRegistry', 'Search registry')}
128
+ </Button>
129
+ </Tile>
130
+ </form>
131
+ );
132
+ };
133
+
134
+ export default HIEClientRegistry;
@@ -0,0 +1,53 @@
1
+ @use '@carbon/type';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/colors';
4
+
5
+ .hieContainer {
6
+ margin-bottom: layout.$layout-01;
7
+ }
8
+
9
+ .productiveHeading02 {
10
+ @include type.type-style('heading-compact-02');
11
+ }
12
+
13
+ .label01 {
14
+ @include type.type-style('label-01');
15
+ margin-top: layout.$spacing-05;
16
+ margin-bottom: layout.$spacing-05;
17
+ color: colors.$gray-50;
18
+ }
19
+
20
+ /* Tablet viewport */
21
+ :global(.omrs-breakpoint-lt-desktop) {
22
+ .grid {
23
+ display: flex;
24
+ flex-direction: column;
25
+ row-gap: layout.$spacing-05;
26
+ margin: layout.$spacing-01 0;
27
+ padding-bottom: layout.$layout-02;
28
+ column-gap: layout.$spacing-05;
29
+
30
+ & > button {
31
+ width: 50%;
32
+ }
33
+ }
34
+ }
35
+
36
+ /* Desktop viewport */
37
+ :global(.omrs-breakpoint-gt-tablet) {
38
+ .grid {
39
+ margin: layout.$spacing-01 0;
40
+ padding-bottom: layout.$layout-02;
41
+ display: grid;
42
+ grid-template-columns: 1fr 1fr 0.5fr;
43
+ column-gap: layout.$spacing-05;
44
+ align-items: flex-start;
45
+
46
+ & > button {
47
+ align-self: flex-start;
48
+ margin-top: 1.4rem;
49
+ max-width: 14rem;
50
+ min-width: 14rem;
51
+ }
52
+ }
53
+ }
@@ -0,0 +1,160 @@
1
+ import { add, capitalize } from 'lodash-es';
2
+ import { type PatientIdentifierValue, type FormValues } from '../../patient-registration/patient-registration.types';
3
+ import { type MapperConfig, type HIEPatient, type ErrorResponse } from './hie-types';
4
+ import { getConfig } from '@openmrs/esm-framework';
5
+ import { type RegistrationConfig } from '../../config-schema';
6
+ import { v4 } from 'uuid';
7
+ /**
8
+ * Represents a client for interacting with a Health Information Exchange (HIE) resource.
9
+ * @template T - The type of the resource being fetched.
10
+ */
11
+ class HealthInformationExchangeClient<T> {
12
+ async fetchResource(resourceType: string, params: Record<string, string>): Promise<T> {
13
+ const {
14
+ hieClientRegistry: { baseUrl, encodedCredentials },
15
+ } = await getConfig<RegistrationConfig>('@kenyaemr/esm-patient-registration-app');
16
+ const urlParams = new URLSearchParams(params);
17
+ const response = await fetch(`${baseUrl}${resourceType}?${urlParams}`, {
18
+ headers: new Headers({
19
+ Authorization: `Basic ${encodedCredentials}`,
20
+ }),
21
+ });
22
+
23
+ return response.json();
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Represents a generic Mapper class.
29
+ * @template T - The source type.
30
+ * @template U - The target type.
31
+ */
32
+ class Mapper<T, U> {
33
+ protected config: MapperConfig;
34
+
35
+ constructor(config: MapperConfig) {
36
+ this.config = config;
37
+ }
38
+ }
39
+
40
+ /**
41
+ * Maps HIEPatient objects to FormValues objects.
42
+ */
43
+ class PatientMapper extends Mapper<HIEPatient, FormValues> {
44
+ mapHIEPatientToFormValues(hiePatient: HIEPatient, currentFormValues: FormValues): FormValues {
45
+ const name = hiePatient.name?.[0] || {};
46
+ const telecom = hiePatient.telecom || [];
47
+
48
+ const telecomAttributes = this.mapTelecomToAttributes(telecom);
49
+ const updatedIdentifiers = this.mapIdentifiers(hiePatient, currentFormValues);
50
+ const extensionAddressEntries = this.mapExtensionsToAddress(hiePatient.extension);
51
+
52
+ return {
53
+ isDead: hiePatient.deceasedBoolean || false,
54
+ gender: hiePatient.gender || '',
55
+ birthdate: hiePatient.birthDate || '',
56
+ givenName: name.given?.[0] || '',
57
+ familyName: name.family || '',
58
+ telephoneNumber: telecom.find((t) => t.system === 'phone')?.value || '',
59
+ middleName: name.given?.[1],
60
+ address: extensionAddressEntries,
61
+ identifiers: updatedIdentifiers,
62
+ attributes: telecomAttributes,
63
+ relationships: [],
64
+ patientUuid: v4(),
65
+ } as FormValues;
66
+ }
67
+
68
+ private mapTelecomToAttributes(telecom: Array<fhir.ContactPoint>): Record<string, string> {
69
+ return telecom.reduce<Record<string, string>>((acc, { system, value }) => {
70
+ if (system && value && this.config.teleComMap[system]) {
71
+ const filteredValue = value.replace(/^254/, '0');
72
+ if (filteredValue) {
73
+ acc[this.config.teleComMap[system]] = filteredValue;
74
+ }
75
+ }
76
+ return acc;
77
+ }, {});
78
+ }
79
+
80
+ private mapIdentifiers(
81
+ hiePatient: HIEPatient,
82
+ currentFormValues: FormValues,
83
+ ): Record<string, PatientIdentifierValue> {
84
+ const updatedIdentifiers: Record<string, PatientIdentifierValue> = { ...currentFormValues.identifiers };
85
+
86
+ // Map Social Health Authority Unique Identification Number to HIE Patient ID
87
+ // See https://github.com/palladiumkenya/openmrs-module-kenyaemr/blob/1e1d281eaba8041c45318e60ca0730449b8e4197/api/src/main/distro/metadata/identifierTypes.xml#L33
88
+ updatedIdentifiers.socialHealthAuthorityIdentificationNumber = {
89
+ ...currentFormValues.identifiers['socialHealthAuthorityIdentificationNumber'],
90
+ identifierValue: hiePatient.id,
91
+ };
92
+
93
+ // Map fhir.Patient.Identifier to identifiers
94
+ hiePatient.identifier?.forEach((identifier) => {
95
+ const system = identifier.system?.split('/').pop();
96
+ if (system && this.config.identifierMap[system]) {
97
+ const key = this.config.identifierMap[system];
98
+ updatedIdentifiers[key] = {
99
+ ...currentFormValues.identifiers[key],
100
+ identifierValue: identifier.value || '',
101
+ };
102
+ }
103
+ });
104
+
105
+ // Filter out undefined keys and values
106
+ Object.keys(updatedIdentifiers).forEach((key) => {
107
+ if (updatedIdentifiers[key] === undefined || updatedIdentifiers[key].identifierValue === undefined) {
108
+ delete updatedIdentifiers[key];
109
+ }
110
+ });
111
+
112
+ return updatedIdentifiers;
113
+ }
114
+
115
+ private mapExtensionsToAddress(extensions: HIEPatient['extension']): Record<string, string> {
116
+ return extensions.reduce<Record<string, string>>((acc, ext) => {
117
+ const identifierType = ext.url.split('/').pop();
118
+ const mappedKey = this.config.addressHierarchyMap[identifierType];
119
+
120
+ if (mappedKey && ext.valueString) {
121
+ acc[mappedKey] = capitalize(ext.valueString);
122
+ }
123
+
124
+ return acc;
125
+ }, {});
126
+ }
127
+ }
128
+
129
+ // Update MapperConfig interface in hie-types.ts
130
+
131
+ const mapperConfig: MapperConfig = {
132
+ teleComMap: {
133
+ email: 'b8d0b331-1d2d-4a9a-b741-1816f498bdb6',
134
+ phone: 'b2c38640-2603-4629-aebd-3b54f33f1e3a',
135
+ },
136
+ addressHierarchyMap: {
137
+ county: 'countyDistrict',
138
+ 'sub-county': 'stateProvince',
139
+ ward: 'address4',
140
+ },
141
+ addressMap: {},
142
+ // Map FHIR Patient identifiers to identifiers, at the moment HIE teams returns null for all identifiers type codings
143
+ identifierMap: {},
144
+ };
145
+
146
+ // Create instances
147
+ const hieApiClient = new HealthInformationExchangeClient<HIEPatient | ErrorResponse>();
148
+ const patientMapper = new PatientMapper(mapperConfig);
149
+
150
+ // Exported functions
151
+ export const fetchPatientFromHIE = async (
152
+ identifierType: string,
153
+ identifierValue: string,
154
+ ): Promise<HIEPatient | ErrorResponse> => {
155
+ return hieApiClient.fetchResource('Patient', { [identifierType]: identifierValue });
156
+ };
157
+
158
+ export const mapHIEPatientToFormValues = (hiePatient: HIEPatient, currentFormValues: FormValues): FormValues => {
159
+ return patientMapper.mapHIEPatientToFormValues(hiePatient, currentFormValues);
160
+ };
@@ -0,0 +1,29 @@
1
+ export type HIEPatient = fhir.Patient & {
2
+ extension: Array<{
3
+ url: string;
4
+ valueString: string;
5
+ }>;
6
+ };
7
+
8
+ export type APIClientConfig = {
9
+ baseUrl: string;
10
+ credentials: string;
11
+ };
12
+
13
+ export interface MapperConfig {
14
+ teleComMap: Record<string, string>;
15
+ addressHierarchyMap: Record<string, string>;
16
+ identifierMap: Record<string, string>;
17
+ addressMap: Record<string, string>;
18
+ }
19
+
20
+ export type ErrorResponse = {
21
+ resourceType: string;
22
+ issue: Array<Issue>;
23
+ };
24
+
25
+ export type Issue = {
26
+ severity: string;
27
+ code: string;
28
+ diagnostics: string;
29
+ };
@@ -0,0 +1,82 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button, ModalBody, ModalHeader, ModalFooter, Accordion, AccordionItem, CodeSnippet } from '@carbon/react';
4
+ import styles from './confirm-hie.scss';
5
+ import { age, ExtensionSlot, formatDate } from '@openmrs/esm-framework';
6
+ import { type HIEPatient } from '../hie-types';
7
+ import capitalize from 'lodash-es/capitalize';
8
+
9
+ const PatientInfo: React.FC<{ label: string; value: string }> = ({ label, value }) => {
10
+ return (
11
+ <div style={{ display: 'grid', gridTemplateColumns: '0.25fr 0.75fr', margin: '0.25rem' }}>
12
+ <span style={{ minWidth: '5rem', fontWeight: 'bold' }}>{label}</span>
13
+ <span>{value}</span>
14
+ </div>
15
+ );
16
+ };
17
+
18
+ interface HIEConfirmationModalProps {
19
+ closeModal: () => void;
20
+ patient: HIEPatient;
21
+ onUseValues: () => void;
22
+ }
23
+
24
+ const HIEConfirmationModal: React.FC<HIEConfirmationModalProps> = ({ closeModal, patient, onUseValues }) => {
25
+ const { t } = useTranslation();
26
+ const firstName = patient?.name[0]['given']?.[0];
27
+ const lastName = patient?.name[0]['family'];
28
+
29
+ const handleUseValues = () => {
30
+ onUseValues();
31
+ closeModal();
32
+ };
33
+
34
+ return (
35
+ <div>
36
+ <ModalHeader closeModal={closeModal}>
37
+ <span className={styles.header}>{t('hieModal', 'HIE Patient Record Found')}</span>
38
+ </ModalHeader>
39
+ <ModalBody>
40
+ <div style={{ display: 'flex', margin: '1rem' }}>
41
+ <ExtensionSlot
42
+ style={{ display: 'flex', alignItems: 'center' }}
43
+ name="patient-photo-slot"
44
+ state={{ patientName: `${firstName} ${lastName}` }}
45
+ />
46
+ <div style={{ width: '100%', marginLeft: '0.625rem' }}>
47
+ <PatientInfo label={t('healthID', 'HealthID')} value={patient?.id} />
48
+ <PatientInfo label={t('patientName', 'Patient name')} value={`${firstName} ${lastName}`} />
49
+ <PatientInfo label={t('age', 'Age')} value={age(patient?.birthDate)} />
50
+ <PatientInfo label={t('dateOfBirth', 'Date of birth')} value={formatDate(new Date(patient?.birthDate))} />
51
+ <PatientInfo label={t('gender', 'Gender')} value={capitalize(patient?.gender)} />
52
+ <PatientInfo
53
+ label={t('maritalStatus', 'Marital status')}
54
+ value={patient?.maritalStatus?.coding?.map((m) => m.code).join('')}
55
+ />
56
+ <PatientInfo label={t('dependents', 'Dependents')} value="--" />
57
+ </div>
58
+ </div>
59
+ <div>
60
+ <Accordion>
61
+ <AccordionItem title={t('viewFullResponse', 'View full response')}>
62
+ <CodeSnippet type="multi" feedback="Copied to clipboard">
63
+ {JSON.stringify(patient, null, 2)}
64
+ </CodeSnippet>
65
+ </AccordionItem>
66
+ </Accordion>
67
+ </div>
68
+ </ModalBody>
69
+ <ModalFooter>
70
+ <Button kind="secondary" onClick={closeModal}>
71
+ {t('cancel', 'Cancel')}
72
+ </Button>
73
+
74
+ <Button onClick={handleUseValues} kind="primary">
75
+ {t('useValues', 'Use values')}
76
+ </Button>
77
+ </ModalFooter>
78
+ </div>
79
+ );
80
+ };
81
+
82
+ export default HIEConfirmationModal;
@@ -0,0 +1,10 @@
1
+ @use '@carbon/type';
2
+ @use '@openmrs/esm-styleguide/src/vars' as *;
3
+
4
+ .header {
5
+ @include type.type-style('heading-03');
6
+ }
7
+
8
+ .body {
9
+ @include type.type-style('body-01');
10
+ }
@@ -6,7 +6,7 @@ import {
6
6
  type ConceptAnswers,
7
7
  type ConceptResponse,
8
8
  type FormValues,
9
- } from '../patient-registration/patient-registration.types';
9
+ } from '../../patient-registration/patient-registration.types';
10
10
 
11
11
  export function searchClientRegistry(
12
12
  identifierType: string,
@@ -108,7 +108,7 @@ export function useGlobalProperties() {
108
108
  const { data, isLoading, error } = useSWRImmutable(
109
109
  `https://afyakenyaidentityapi.health.go.ke/connect/token`,
110
110
  swrFetcher,
111
- { refreshInterval: 864000 },
111
+ { refreshInterval: 864000, errorRetryCount: 0 },
112
112
  );
113
113
  return { data: data, isLoading, error };
114
114
  }
@@ -2,7 +2,7 @@ import { showModal } from '@openmrs/esm-framework';
2
2
  import { type FormikProps } from 'formik';
3
3
  import { type ClientRegistryPatient, type RegistryPatient } from './verification-types';
4
4
  import counties from './assets/counties.json';
5
- import { type FormValues } from '../patient-registration/patient-registration.types';
5
+ import { type FormValues } from '../../patient-registration/patient-registration.types';
6
6
  import { capitalize } from 'lodash-es';
7
7
 
8
8
  export function handleClientRegistryResponse(
@@ -7,7 +7,7 @@ import { searchClientRegistry, useGlobalProperties } from './patient-verificatio
7
7
  import { showSnackbar, showToast } from '@openmrs/esm-framework';
8
8
  import { handleClientRegistryResponse } from './patient-verification-utils';
9
9
  import { type FormikProps } from 'formik';
10
- import { type FormValues } from '../patient-registration/patient-registration.types';
10
+ import { type FormValues } from '../../patient-registration/patient-registration.types';
11
11
 
12
12
  interface PatientVerificationProps {
13
13
  props: FormikProps<FormValues>;
@@ -58,6 +58,9 @@ const PatientVerification: React.FC<PatientVerificationProps> = ({ props }) => {
58
58
  <h3 className={styles.productiveHeading02} style={{ color: '#161616' }}>
59
59
  {t('clientVerificationWithClientRegistry', 'Client verification with client registry')}
60
60
  </h3>
61
+ <span className={styles.label01}>
62
+ {t('allFieldsRequiredText', 'All fields are required unless marked optional')}
63
+ </span>
61
64
  <div style={{ margin: '1rem 0 1rem' }}>
62
65
  <Layer>
63
66
  {isLoading && <InlineLoading status="active" iconDescription="Loading" description="Loading data..." />}
@@ -1,5 +1,8 @@
1
1
  @use '@carbon/colors';
2
- @import '../patient-registration/patient-registration.scss';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+ @use '../../patient-registration/patient-registration.scss' as *;
3
6
 
4
7
  /* Desktop */
5
8
  :global(.omrs-breakpoint-gt-tablet) {
@@ -23,3 +26,16 @@
23
26
  .errorWrapper {
24
27
  margin: 0 0 1rem 0;
25
28
  }
29
+
30
+ .label01 {
31
+ @include type.type-style('label-01');
32
+ margin-top: layout.$spacing-05;
33
+ margin-bottom: layout.$spacing-05;
34
+ color: $ui-04;
35
+ }
36
+
37
+ .productiveHeading02 {
38
+ @include type.type-style('heading-compact-02');
39
+ color: $ui-04;
40
+ cursor: pointer;
41
+ }
@@ -5,21 +5,24 @@ import { Button } from '@carbon/react';
5
5
  interface EmptyPromptProps {
6
6
  onConfirm: void;
7
7
  close: void;
8
+ title?: string;
9
+ message?: string;
8
10
  }
9
11
 
10
- const EmptyPrompt: React.FC<EmptyPromptProps> = ({ close, onConfirm }) => {
12
+ const EmptyPrompt: React.FC<EmptyPromptProps> = ({ close, onConfirm, title, message }) => {
11
13
  const { t } = useTranslation();
12
14
  return (
13
15
  <>
14
16
  <div className="cds--modal-header">
15
- <h3 className="cds--modal-header__heading">{t('clientRegistryEmpty', 'Create & Post Patient')}</h3>
17
+ <h3 className="cds--modal-header__heading">{title ?? t('clientRegistryEmpty', 'Create & Post Patient')}</h3>
16
18
  </div>
17
19
  <div className="cds--modal-content">
18
20
  <p>
19
- {t(
20
- 'patientNotFound',
21
- 'The patient records could not be found in Client registry, do you want to continue to create and post patient to registry',
22
- )}
21
+ {message ??
22
+ t(
23
+ 'patientNotFound',
24
+ 'The patient records could not be found in Client registry, do you want to continue to create and post patient to registry',
25
+ )}
23
26
  </p>
24
27
  </div>
25
28
  <div className="cds--modal-footer">