@kenyaemr/esm-version-app 5.4.3 → 5.4.4-pre.100
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/.turbo/turbo-build.log +6 -6
- package/dist/1074.js +1 -0
- package/dist/1074.js.map +1 -0
- package/dist/1311.js +1 -0
- package/dist/1311.js.map +1 -0
- package/dist/1469.js +1 -0
- package/dist/1469.js.map +1 -0
- package/dist/1718.js +1 -0
- package/dist/1718.js.map +1 -0
- package/dist/1772.js +1 -0
- package/dist/1772.js.map +1 -0
- package/dist/1889.js +1 -0
- package/dist/1889.js.map +1 -0
- package/dist/1972.js +1 -0
- package/dist/1972.js.map +1 -0
- package/dist/1990.js +1 -0
- package/dist/1990.js.map +1 -0
- package/dist/2016.js +1 -0
- package/dist/2016.js.map +1 -0
- package/dist/2119.js +1 -0
- package/dist/2119.js.map +1 -0
- package/dist/2153.js +1 -0
- package/dist/2153.js.map +1 -0
- package/dist/216.js +1 -0
- package/dist/216.js.map +1 -0
- package/dist/2294.js +1 -0
- package/dist/2294.js.map +1 -0
- package/dist/2345.js +1 -0
- package/dist/2345.js.map +1 -0
- package/dist/2500.js +1 -0
- package/dist/2500.js.map +1 -0
- package/dist/251.js +1 -0
- package/dist/251.js.map +1 -0
- package/dist/257.js +1 -0
- package/dist/257.js.map +1 -0
- package/dist/2586.js +1 -0
- package/dist/2586.js.map +1 -0
- package/dist/2625.js +1 -0
- package/dist/2625.js.map +1 -0
- package/dist/2685.js +1 -0
- package/dist/2685.js.map +1 -0
- package/dist/2948.js +1 -0
- package/dist/2948.js.map +1 -0
- package/dist/3089.js +1 -0
- package/dist/3089.js.map +1 -0
- package/dist/3190.js +1 -0
- package/dist/3190.js.map +1 -0
- package/dist/3548.js +1 -0
- package/dist/3548.js.map +1 -0
- package/dist/3816.js +1 -0
- package/dist/3816.js.map +1 -0
- package/dist/3906.js +1 -0
- package/dist/3906.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/405.js +1 -0
- package/dist/405.js.map +1 -0
- package/dist/4296.js +1 -0
- package/dist/4296.js.map +1 -0
- package/dist/4368.js +1 -0
- package/dist/4368.js.map +1 -0
- package/dist/4537.js +1 -0
- package/dist/4537.js.map +1 -0
- package/dist/4735.js +1 -0
- package/dist/4735.js.map +1 -0
- package/dist/4744.js +1 -0
- package/dist/4744.js.map +1 -0
- package/dist/4858.js +1 -0
- package/dist/4858.js.map +1 -0
- package/dist/487.js +1 -0
- package/dist/487.js.map +1 -0
- package/dist/5128.js +1 -0
- package/dist/5128.js.map +1 -0
- package/dist/5192.js +17 -0
- package/dist/5192.js.map +1 -0
- package/dist/5202.js +1 -0
- package/dist/5202.js.map +1 -0
- package/dist/5592.js +1 -0
- package/dist/5592.js.map +1 -0
- package/dist/5669.js +1 -0
- package/dist/5669.js.map +1 -0
- package/dist/5795.js +1 -0
- package/dist/5795.js.map +1 -0
- package/dist/5880.js +1 -0
- package/dist/5880.js.map +1 -0
- package/dist/6178.js +1 -0
- package/dist/6178.js.map +1 -0
- package/dist/6373.js +13 -0
- package/dist/6373.js.map +1 -0
- package/dist/6456.js +1 -0
- package/dist/6492.js +1 -0
- package/dist/6492.js.map +1 -0
- package/dist/6495.js +1 -0
- package/dist/6495.js.map +1 -0
- package/dist/6561.js +1 -0
- package/dist/6561.js.map +1 -0
- package/dist/6800.js +1 -0
- package/dist/6800.js.map +1 -0
- package/dist/6962.js +1 -0
- package/dist/6962.js.map +1 -0
- package/dist/7005.js +1 -0
- package/dist/7005.js.map +1 -0
- package/dist/7201.js +1 -0
- package/dist/7201.js.map +1 -0
- package/dist/7234.js +1 -0
- package/dist/7234.js.map +1 -0
- package/dist/7261.js +1 -0
- package/dist/7261.js.map +1 -0
- package/dist/7326.js +1 -0
- package/dist/7463.js +1 -0
- package/dist/7463.js.map +1 -0
- package/dist/7497.js +1 -0
- package/dist/7497.js.map +1 -0
- package/dist/7607.js +1 -0
- package/dist/7626.js +1 -0
- package/dist/7626.js.map +1 -0
- package/dist/7701.js +1 -0
- package/dist/7701.js.map +1 -0
- package/dist/7717.js +1 -0
- package/dist/7717.js.map +1 -0
- package/dist/7739.js +1 -0
- package/dist/7739.js.map +1 -0
- package/dist/7765.js +1 -0
- package/dist/7765.js.map +1 -0
- package/dist/7971.js +1 -0
- package/dist/7971.js.map +1 -0
- package/dist/8159.js +7 -0
- package/dist/8159.js.map +1 -0
- package/dist/8244.js +1 -0
- package/dist/8244.js.map +1 -0
- package/dist/8252.js +4 -0
- package/dist/8252.js.map +1 -0
- package/dist/845.js +1 -0
- package/dist/845.js.map +1 -0
- package/dist/8570.js +1 -0
- package/dist/8570.js.map +1 -0
- package/dist/87.js +1 -0
- package/dist/87.js.map +1 -0
- package/dist/8727.js +1 -0
- package/dist/9124.js +1 -0
- package/dist/9124.js.map +1 -0
- package/dist/9182.js +1 -0
- package/dist/921.js +1 -0
- package/dist/921.js.map +1 -0
- package/dist/9404.js +1 -0
- package/dist/9404.js.map +1 -0
- package/dist/9446.js +1 -0
- package/dist/9446.js.map +1 -0
- package/dist/9449.js +1 -0
- package/dist/9449.js.map +1 -0
- package/dist/9566.js +5 -0
- package/dist/9566.js.map +1 -0
- package/dist/9641.js +1 -0
- package/dist/9641.js.map +1 -0
- package/dist/9801.js +1 -0
- package/dist/9801.js.map +1 -0
- package/dist/9835.js +11 -0
- package/dist/9835.js.map +1 -0
- package/dist/9900.js +1 -0
- package/dist/9900.js.map +1 -0
- package/dist/main.js +5 -5
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-verification-app.js +5 -5
- package/dist/openmrs-esm-patient-verification-app.js.buildmanifest.json +1917 -111
- package/dist/openmrs-esm-patient-verification-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +5 -7
- package/rspack.config.js +1 -1
- package/src/about/about.component.tsx +195 -27
- package/src/about/about.scss +57 -9
- package/src/config-schema.ts +10 -1
- package/src/frontend-modules/frontend-modules.component.test.tsx +62 -0
- package/src/frontend-modules/frontend-modules.component.tsx +47 -24
- package/src/hooks/useModules.tsx +9 -0
- package/src/index.ts +2 -3
- package/src/release-version.js +8 -8
- package/src/types/index.ts +15 -0
- package/translations/am.json +11 -1
- package/translations/en.json +11 -1
- package/translations/sw.json +11 -1
- package/tsconfig.json +1 -1
- package/dist/127.js +0 -1
- package/dist/129.js +0 -1
- package/dist/129.js.map +0 -1
- package/dist/267.js +0 -1
- package/dist/267.js.map +0 -1
- package/dist/286.js +0 -1
- package/dist/286.js.map +0 -1
- package/dist/40.js +0 -1
- package/dist/424.js +0 -43
- package/dist/424.js.map +0 -1
- package/dist/466.js +0 -1
- package/dist/466.js.map +0 -1
- package/dist/478.js +0 -1
- package/dist/478.js.map +0 -1
- package/dist/501.js +0 -1
- package/dist/675.js +0 -1
- package/dist/675.js.map +0 -1
- package/dist/689.js +0 -1
- package/dist/689.js.map +0 -1
- package/dist/916.js +0 -1
- package/dist/94.js +0 -15
- package/dist/94.js.map +0 -1
- package/dist/998.js +0 -1
- package/dist/998.js.map +0 -1
- package/jest.config.js +0 -3
|
@@ -1,45 +1,213 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import type { TFunction } from 'i18next';
|
|
4
|
+
import { formatDatetime, useConfig } from '@openmrs/esm-framework';
|
|
5
|
+
import { InlineLoading } from '@carbon/react';
|
|
6
|
+
import { Phone, Email, Location, Globe, WarningAlt } from '@carbon/react/icons';
|
|
7
|
+
|
|
2
8
|
import styles from './about.scss';
|
|
3
|
-
import { useModules } from '../hooks/useModules';
|
|
4
|
-
import { formatDate, formatDatetime, showNotification } from '@openmrs/esm-framework';
|
|
9
|
+
import { useModules, useGlobalProperty } from '../hooks/useModules';
|
|
5
10
|
import FrontendModule from '../frontend-modules/frontend-modules.component';
|
|
11
|
+
import type { FacilityContacts, FacilityInformation } from '../types';
|
|
12
|
+
|
|
6
13
|
const packageInfo = require('../release-version.js');
|
|
7
14
|
|
|
15
|
+
function parseFacilityInformation(value: unknown): FacilityInformation | null {
|
|
16
|
+
if (value == null) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
21
|
+
return value as FacilityInformation;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(value) as FacilityInformation;
|
|
27
|
+
} catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface FacilityHeaderProps {
|
|
36
|
+
facilityName: string;
|
|
37
|
+
taglineText?: string;
|
|
38
|
+
logoSrc: string;
|
|
39
|
+
titleClassName: string;
|
|
40
|
+
taglineClassName: string;
|
|
41
|
+
logoAlt: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const FacilityHeader: React.FC<FacilityHeaderProps> = ({
|
|
45
|
+
facilityName,
|
|
46
|
+
taglineText,
|
|
47
|
+
logoSrc,
|
|
48
|
+
titleClassName,
|
|
49
|
+
taglineClassName,
|
|
50
|
+
logoAlt,
|
|
51
|
+
}) => (
|
|
52
|
+
<div className={titleClassName}>
|
|
53
|
+
<img src={logoSrc} alt={logoAlt} width="300" height="100" />
|
|
54
|
+
<div>
|
|
55
|
+
<h3>{facilityName}</h3>
|
|
56
|
+
{taglineText ? <p className={taglineClassName}>{taglineText}</p> : null}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
interface FacilityContactsProps {
|
|
62
|
+
contacts?: FacilityContacts;
|
|
63
|
+
contactsClassName: string;
|
|
64
|
+
contactItemClassName: string;
|
|
65
|
+
t: TFunction;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const FacilityContactsSection: React.FC<FacilityContactsProps> = ({
|
|
69
|
+
contacts,
|
|
70
|
+
contactsClassName,
|
|
71
|
+
contactItemClassName,
|
|
72
|
+
t,
|
|
73
|
+
}) => {
|
|
74
|
+
if (
|
|
75
|
+
!contacts ||
|
|
76
|
+
(!contacts.tel && !contacts.email && !contacts.emergency && !contacts.address && !contacts.website)
|
|
77
|
+
) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div className={contactsClassName}>
|
|
83
|
+
{contacts.tel && (
|
|
84
|
+
<div className={contactItemClassName}>
|
|
85
|
+
<Phone size={16} />
|
|
86
|
+
<span>{contacts.tel}</span>
|
|
87
|
+
</div>
|
|
88
|
+
)}
|
|
89
|
+
{contacts.email && (
|
|
90
|
+
<div className={contactItemClassName}>
|
|
91
|
+
<Email size={16} />
|
|
92
|
+
<a href={`mailto:${contacts.email}`}>{contacts.email}</a>
|
|
93
|
+
</div>
|
|
94
|
+
)}
|
|
95
|
+
{contacts.emergency && (
|
|
96
|
+
<div className={contactItemClassName}>
|
|
97
|
+
<WarningAlt size={16} />
|
|
98
|
+
<span>
|
|
99
|
+
{t('emergency', 'Emergency')}: {contacts.emergency}
|
|
100
|
+
</span>
|
|
101
|
+
</div>
|
|
102
|
+
)}
|
|
103
|
+
{contacts.address && (
|
|
104
|
+
<div className={contactItemClassName}>
|
|
105
|
+
<Location size={16} />
|
|
106
|
+
<span>{contacts.address}</span>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
{contacts.website && (
|
|
110
|
+
<div className={contactItemClassName}>
|
|
111
|
+
<Globe size={16} />
|
|
112
|
+
<a
|
|
113
|
+
href={contacts.website.startsWith('http') ? contacts.website : `https://${contacts.website}`}
|
|
114
|
+
target="_blank"
|
|
115
|
+
rel="noopener noreferrer">
|
|
116
|
+
{contacts.website}
|
|
117
|
+
</a>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
interface VersionDetailsProps {
|
|
125
|
+
kenyaEmrVersion?: string;
|
|
126
|
+
spaVersion: string;
|
|
127
|
+
buildDate: Date;
|
|
128
|
+
aboutBodyClassName: string;
|
|
129
|
+
t: TFunction;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const VersionDetails: React.FC<VersionDetailsProps> = ({
|
|
133
|
+
kenyaEmrVersion,
|
|
134
|
+
spaVersion,
|
|
135
|
+
buildDate,
|
|
136
|
+
aboutBodyClassName,
|
|
137
|
+
t,
|
|
138
|
+
}) => (
|
|
139
|
+
<div className={aboutBodyClassName}>
|
|
140
|
+
<p>{t('mainModuleVersion', 'Main Module Version')}</p>
|
|
141
|
+
<p>{`v${kenyaEmrVersion ?? '—'}`}</p>
|
|
142
|
+
<p>{t('spaVersion', 'SPA Version')}</p>
|
|
143
|
+
<p>{`v${spaVersion}`}</p>
|
|
144
|
+
<p>{t('buildDateTime', 'Build date time')}</p>
|
|
145
|
+
<p>{formatDatetime(buildDate, { mode: 'standard' })}</p>
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
148
|
+
|
|
8
149
|
interface AboutProps {}
|
|
9
150
|
|
|
10
151
|
const About: React.FC<AboutProps> = () => {
|
|
11
|
-
const {
|
|
12
|
-
const
|
|
13
|
-
const {
|
|
152
|
+
const { t } = useTranslation();
|
|
153
|
+
const { modules } = useModules();
|
|
154
|
+
const { defaultLogoPath } = useConfig();
|
|
155
|
+
const { data: facilityInformation, isLoading: isFacilityInformationLoading } = useGlobalProperty(
|
|
156
|
+
'kenyaemr.cashier.receipt.facilityInformation',
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const kenyaEmrModule = modules.find(({ uuid }) => uuid === 'kenyaemr');
|
|
160
|
+
const { title, container, aboutBody, aboutPage, tagline, contacts, contactItem } = styles;
|
|
14
161
|
const { VERSION } = packageInfo;
|
|
15
162
|
|
|
163
|
+
const facility = useMemo(() => parseFacilityInformation(facilityInformation?.['value']), [facilityInformation]);
|
|
164
|
+
|
|
165
|
+
if (isFacilityInformationLoading) {
|
|
166
|
+
return <InlineLoading description={t('loadingFacilityInformation', 'Loading facility information...')} />;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const contactsData = facility?.contacts;
|
|
170
|
+
|
|
171
|
+
const facilityName = facility?.facilityName ?? 'Ministry of Health';
|
|
172
|
+
const facilityTagline = facility?.tagline;
|
|
173
|
+
const logoAlt = facility?.facilityName ?? t('facilityLogo', 'Facility logo');
|
|
174
|
+
|
|
175
|
+
const spaVersion = VERSION.version;
|
|
176
|
+
const buildDate = new Date(VERSION.buildDate);
|
|
177
|
+
const kenyaEmrVersion = kenyaEmrModule?.version;
|
|
178
|
+
|
|
16
179
|
return (
|
|
17
180
|
<div className={styles.main}>
|
|
18
181
|
<div className={aboutPage}>
|
|
19
182
|
<div className={container}>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
183
|
+
<FacilityHeader
|
|
184
|
+
facilityName={facilityName}
|
|
185
|
+
taglineText={facilityTagline}
|
|
186
|
+
logoSrc={defaultLogoPath}
|
|
187
|
+
titleClassName={title}
|
|
188
|
+
taglineClassName={tagline}
|
|
189
|
+
logoAlt={logoAlt}
|
|
190
|
+
/>
|
|
191
|
+
|
|
192
|
+
<FacilityContactsSection
|
|
193
|
+
contacts={contactsData}
|
|
194
|
+
contactsClassName={contacts}
|
|
195
|
+
contactItemClassName={contactItem}
|
|
196
|
+
t={t}
|
|
197
|
+
/>
|
|
198
|
+
|
|
199
|
+
<VersionDetails
|
|
200
|
+
kenyaEmrVersion={kenyaEmrVersion}
|
|
201
|
+
spaVersion={spaVersion}
|
|
202
|
+
buildDate={buildDate}
|
|
203
|
+
aboutBodyClassName={aboutBody}
|
|
204
|
+
t={t}
|
|
205
|
+
/>
|
|
40
206
|
</div>
|
|
41
207
|
</div>
|
|
42
|
-
<
|
|
208
|
+
<div className={styles.frontendModules}>
|
|
209
|
+
<FrontendModule />
|
|
210
|
+
</div>
|
|
43
211
|
</div>
|
|
44
212
|
);
|
|
45
213
|
};
|
package/src/about/about.scss
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
@use '@carbon/styles/scss/spacing';
|
|
2
2
|
@use '@carbon/styles/scss/type';
|
|
3
3
|
@use '@carbon/styles/scss/colors';
|
|
4
|
+
@use '@carbon/layout';
|
|
4
5
|
|
|
5
6
|
.main {
|
|
6
7
|
display: flex;
|
|
@@ -12,6 +13,7 @@
|
|
|
12
13
|
display: flex;
|
|
13
14
|
justify-content: center;
|
|
14
15
|
align-items: center;
|
|
16
|
+
width: 100%;
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
.container {
|
|
@@ -21,28 +23,69 @@
|
|
|
21
23
|
flex-direction: column;
|
|
22
24
|
align-items: center;
|
|
23
25
|
border: 0.5px solid colors.$gray-30;
|
|
24
|
-
width:
|
|
26
|
+
width: 60rem;
|
|
27
|
+
padding: layout.$spacing-05;
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
.title {
|
|
28
31
|
display: flex;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
margin-bottom: 0.25rem;
|
|
32
|
+
padding-bottom: layout.$spacing-03;
|
|
33
|
+
margin-bottom: layout.$spacing-03;
|
|
32
34
|
border-bottom: 0.5px solid colors.$gray-30;
|
|
33
35
|
width: 100%;
|
|
34
36
|
justify-content: center;
|
|
37
|
+
flex-direction: column;
|
|
38
|
+
align-items: center;
|
|
35
39
|
|
|
36
40
|
h3 {
|
|
37
|
-
@include type.type-style('
|
|
41
|
+
@include type.type-style('heading-compact-01');
|
|
42
|
+
text-align: center;
|
|
43
|
+
margin: 0 0 layout.$spacing-02;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
img {
|
|
47
|
+
margin-bottom: layout.$spacing-03;
|
|
48
|
+
object-fit: contain;
|
|
42
49
|
}
|
|
50
|
+
}
|
|
43
51
|
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
.tagline {
|
|
53
|
+
@include type.type-style('body-compact-01');
|
|
54
|
+
text-align: center;
|
|
55
|
+
color: colors.$gray-70;
|
|
56
|
+
margin: 0;
|
|
57
|
+
max-width: 28rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.contacts {
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
gap: layout.$spacing-02;
|
|
64
|
+
width: 100%;
|
|
65
|
+
padding: layout.$spacing-03 0;
|
|
66
|
+
margin-bottom: layout.$spacing-03;
|
|
67
|
+
border-bottom: 0.5px solid colors.$gray-30;
|
|
68
|
+
align-items: center;
|
|
69
|
+
|
|
70
|
+
a {
|
|
71
|
+
color: colors.$blue-60;
|
|
72
|
+
text-decoration: none;
|
|
73
|
+
|
|
74
|
+
&:hover {
|
|
75
|
+
text-decoration: underline;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.contactItem {
|
|
81
|
+
display: flex;
|
|
82
|
+
align-items: center;
|
|
83
|
+
gap: layout.$spacing-02;
|
|
84
|
+
@include type.type-style('body-compact-01');
|
|
85
|
+
|
|
86
|
+
svg {
|
|
87
|
+
flex-shrink: 0;
|
|
88
|
+
color: colors.$gray-70;
|
|
46
89
|
}
|
|
47
90
|
}
|
|
48
91
|
|
|
@@ -56,3 +99,8 @@
|
|
|
56
99
|
margin-bottom: 0.25rem;
|
|
57
100
|
}
|
|
58
101
|
}
|
|
102
|
+
|
|
103
|
+
.frontendModules {
|
|
104
|
+
width: 100%;
|
|
105
|
+
padding: layout.$spacing-05;
|
|
106
|
+
}
|
package/src/config-schema.ts
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { Type } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export const configSchema = {
|
|
4
|
+
defaultLogoPath: {
|
|
5
|
+
_type: Type.String,
|
|
6
|
+
_default: '/openmrs/spa/kenyaemr-login-logo_poweredby_kenyaemr.png',
|
|
7
|
+
_description:
|
|
8
|
+
'Path to the default facility logo (relative to SPA base), used when facility information has no logo.',
|
|
9
|
+
},
|
|
10
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, within } from '@testing-library/react';
|
|
3
|
+
import { describe, it, beforeEach, expect, vi } from 'vitest';
|
|
4
|
+
|
|
5
|
+
import FrontendModule from './frontend-modules.component';
|
|
6
|
+
import { useFrontendModules } from '../hooks/useFrontendModules';
|
|
7
|
+
|
|
8
|
+
vi.mock('../hooks/useFrontendModules');
|
|
9
|
+
|
|
10
|
+
type UseFrontendModulesMock = ReturnType<typeof vi.fn>;
|
|
11
|
+
|
|
12
|
+
describe('FrontendModule', () => {
|
|
13
|
+
const useFrontendModulesMock = useFrontendModules as unknown as UseFrontendModulesMock;
|
|
14
|
+
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders table headers for module name and version', () => {
|
|
20
|
+
useFrontendModulesMock.mockReturnValue([{ name: 'Test module', version: '1.0.0' }]);
|
|
21
|
+
|
|
22
|
+
render(<FrontendModule />);
|
|
23
|
+
|
|
24
|
+
expect(screen.getByRole('table')).toBeInTheDocument();
|
|
25
|
+
expect(screen.getByRole('columnheader', { name: /module name/i })).toBeInTheDocument();
|
|
26
|
+
expect(screen.getByRole('columnheader', { name: /version/i })).toBeInTheDocument();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('renders a row for each frontend module with name and version', () => {
|
|
30
|
+
useFrontendModulesMock.mockReturnValue([
|
|
31
|
+
{ name: 'Module A', version: '1.0.0' },
|
|
32
|
+
{ name: 'Module B', version: '2.0.0' },
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
render(<FrontendModule />);
|
|
36
|
+
|
|
37
|
+
const table = screen.getByRole('table');
|
|
38
|
+
const rows = within(table).getAllByRole('row');
|
|
39
|
+
|
|
40
|
+
// First row is the header; data rows follow
|
|
41
|
+
const dataRows = rows.slice(1);
|
|
42
|
+
expect(dataRows).toHaveLength(2);
|
|
43
|
+
|
|
44
|
+
expect(within(dataRows[0]).getByText('Module A')).toBeInTheDocument();
|
|
45
|
+
expect(within(dataRows[0]).getByText('1.0.0')).toBeInTheDocument();
|
|
46
|
+
|
|
47
|
+
expect(within(dataRows[1]).getByText('Module B')).toBeInTheDocument();
|
|
48
|
+
expect(within(dataRows[1]).getByText('2.0.0')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('falls back to "No version found" when version is missing', () => {
|
|
52
|
+
useFrontendModulesMock.mockReturnValue([{ name: 'No Version Module' }]);
|
|
53
|
+
|
|
54
|
+
render(<FrontendModule />);
|
|
55
|
+
|
|
56
|
+
const table = screen.getByRole('table');
|
|
57
|
+
const rows = within(table).getAllByRole('row');
|
|
58
|
+
const dataRow = rows[1];
|
|
59
|
+
|
|
60
|
+
expect(within(dataRow).getByText('No version found')).toBeInTheDocument();
|
|
61
|
+
});
|
|
62
|
+
});
|
|
@@ -1,32 +1,55 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
|
+
import { DataTable, Table, TableHead, TableRow, TableHeader, TableBody, TableCell } from '@carbon/react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
|
|
2
5
|
import { useFrontendModules } from '../hooks/useFrontendModules';
|
|
3
|
-
import {
|
|
4
|
-
StructuredListWrapper,
|
|
5
|
-
StructuredListHead,
|
|
6
|
-
StructuredListRow,
|
|
7
|
-
StructuredListCell,
|
|
8
|
-
StructuredListBody,
|
|
9
|
-
} from '@carbon/react';
|
|
10
6
|
|
|
11
7
|
const FrontendModule: React.FC = () => {
|
|
8
|
+
const { t } = useTranslation();
|
|
12
9
|
const installedModules = useFrontendModules();
|
|
10
|
+
|
|
11
|
+
const headers = [
|
|
12
|
+
{ key: 'name', header: t('moduleName', 'Module name') },
|
|
13
|
+
{ key: 'version', header: t('version', 'Version') },
|
|
14
|
+
];
|
|
15
|
+
const rows = useMemo(
|
|
16
|
+
() =>
|
|
17
|
+
installedModules.map((module, index) => ({
|
|
18
|
+
id: `module-${index}`,
|
|
19
|
+
name: module.name,
|
|
20
|
+
version: module.version ?? t('noVersionFound', 'No version found'),
|
|
21
|
+
})),
|
|
22
|
+
[installedModules, t],
|
|
23
|
+
);
|
|
24
|
+
|
|
13
25
|
return (
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
<DataTable
|
|
27
|
+
rows={rows}
|
|
28
|
+
headers={headers}
|
|
29
|
+
size="sm"
|
|
30
|
+
useZebraStyles
|
|
31
|
+
aria-label={t('frontendModules', 'Frontend modules')}>
|
|
32
|
+
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
|
|
33
|
+
<Table {...getTableProps()}>
|
|
34
|
+
<TableHead>
|
|
35
|
+
<TableRow>
|
|
36
|
+
{headers.map((header) => (
|
|
37
|
+
<TableHeader {...getHeaderProps({ header })}>{header.header}</TableHeader>
|
|
38
|
+
))}
|
|
39
|
+
</TableRow>
|
|
40
|
+
</TableHead>
|
|
41
|
+
<TableBody>
|
|
42
|
+
{rows.map((row) => (
|
|
43
|
+
<TableRow {...getRowProps({ row })}>
|
|
44
|
+
{row.cells.map((cell) => (
|
|
45
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
46
|
+
))}
|
|
47
|
+
</TableRow>
|
|
48
|
+
))}
|
|
49
|
+
</TableBody>
|
|
50
|
+
</Table>
|
|
51
|
+
)}
|
|
52
|
+
</DataTable>
|
|
30
53
|
);
|
|
31
54
|
};
|
|
32
55
|
|
package/src/hooks/useModules.tsx
CHANGED
|
@@ -14,3 +14,12 @@ export function useModules() {
|
|
|
14
14
|
|
|
15
15
|
return { modules: data?.data?.results ?? [], isLoading };
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export function useGlobalProperty(property: string) {
|
|
19
|
+
const { data, isLoading } = useSWRImmutable<{ data: { results: Array<OpenmrsResource> } }>(
|
|
20
|
+
`/ws/rest/v1/systemsetting?q=${property}&v=full`,
|
|
21
|
+
openmrsFetch,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
return { data: data?.data?.results?.[0] ?? [], isLoading };
|
|
25
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { defineConfigSchema,
|
|
1
|
+
import { defineConfigSchema, getAsyncLifecycle } from '@openmrs/esm-framework';
|
|
2
2
|
import { configSchema } from './config-schema';
|
|
3
|
-
import rootComponent from './root.component';
|
|
4
3
|
|
|
5
4
|
const moduleName = '@kenyaemr/esm-version-app';
|
|
6
5
|
|
|
@@ -11,7 +10,7 @@ const options = {
|
|
|
11
10
|
|
|
12
11
|
export const importTranslations = require.context('../translations', false, /.json$/, 'lazy');
|
|
13
12
|
|
|
14
|
-
export const about =
|
|
13
|
+
export const about = getAsyncLifecycle(() => import('./root.component'), options);
|
|
15
14
|
|
|
16
15
|
export function startupApp() {
|
|
17
16
|
defineConfigSchema(moduleName, configSchema);
|
package/src/release-version.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
|
3
3
|
/* tslint:disable */
|
|
4
4
|
export const VERSION = {
|
|
5
|
-
"dirty":
|
|
6
|
-
"raw": "v5.4.3-
|
|
7
|
-
"hash": "
|
|
8
|
-
"distance":
|
|
5
|
+
"dirty": true,
|
|
6
|
+
"raw": "v5.4.3-71-g0defd25d-dirty",
|
|
7
|
+
"hash": "g0defd25d",
|
|
8
|
+
"distance": 71,
|
|
9
9
|
"tag": "v5.4.3",
|
|
10
10
|
"semver": {
|
|
11
11
|
"options": {
|
|
@@ -21,9 +21,9 @@ export const VERSION = {
|
|
|
21
21
|
"build": [],
|
|
22
22
|
"version": "5.4.3"
|
|
23
23
|
},
|
|
24
|
-
"suffix": "
|
|
25
|
-
"semverString": "5.4.3",
|
|
26
|
-
"version": "5.4.
|
|
27
|
-
"buildDate": "2026-
|
|
24
|
+
"suffix": "71-g0defd25d-dirty",
|
|
25
|
+
"semverString": "5.4.3+71.g0defd25d",
|
|
26
|
+
"version": "5.4.4-pre.100",
|
|
27
|
+
"buildDate": "2026-04-14T10:33:40.622Z"
|
|
28
28
|
};
|
|
29
29
|
/* tslint:enable */
|
package/src/types/index.ts
CHANGED
|
@@ -11,3 +11,18 @@ export interface DefaultFacility {
|
|
|
11
11
|
shaContracted: string;
|
|
12
12
|
shaFacilityExpiryDate: string;
|
|
13
13
|
}
|
|
14
|
+
|
|
15
|
+
export interface FacilityContacts {
|
|
16
|
+
tel?: string;
|
|
17
|
+
email?: string;
|
|
18
|
+
emergency?: string;
|
|
19
|
+
address?: string;
|
|
20
|
+
website?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface FacilityInformation {
|
|
24
|
+
facilityName?: string;
|
|
25
|
+
tagline?: string;
|
|
26
|
+
logoPath?: string;
|
|
27
|
+
contacts?: FacilityContacts;
|
|
28
|
+
}
|
package/translations/am.json
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"buildDateTime": "Build date time",
|
|
3
|
+
"emergency": "Emergency",
|
|
4
|
+
"facilityLogo": "Facility logo",
|
|
5
|
+
"frontendModules": "Frontend modules",
|
|
6
|
+
"loadingFacilityInformation": "Loading facility information...",
|
|
7
|
+
"mainModuleVersion": "Main Module Version",
|
|
8
|
+
"moduleName": "Module name",
|
|
9
|
+
"noVersionFound": "No version found",
|
|
10
|
+
"spaVersion": "SPA Version",
|
|
11
|
+
"systemInfo": "System Info",
|
|
12
|
+
"version": "Version"
|
|
3
13
|
}
|
package/translations/en.json
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"buildDateTime": "Build date time",
|
|
3
|
+
"emergency": "Emergency",
|
|
4
|
+
"facilityLogo": "Facility logo",
|
|
5
|
+
"frontendModules": "Frontend modules",
|
|
6
|
+
"loadingFacilityInformation": "Loading facility information...",
|
|
7
|
+
"mainModuleVersion": "Main Module Version",
|
|
8
|
+
"moduleName": "Module name",
|
|
9
|
+
"noVersionFound": "No version found",
|
|
10
|
+
"spaVersion": "SPA Version",
|
|
11
|
+
"systemInfo": "System Info",
|
|
12
|
+
"version": "Version"
|
|
3
13
|
}
|
package/translations/sw.json
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
2
|
+
"buildDateTime": "Build date time",
|
|
3
|
+
"emergency": "Emergency",
|
|
4
|
+
"facilityLogo": "Facility logo",
|
|
5
|
+
"frontendModules": "Frontend modules",
|
|
6
|
+
"loadingFacilityInformation": "Loading facility information...",
|
|
7
|
+
"mainModuleVersion": "Main Module Version",
|
|
8
|
+
"moduleName": "Module name",
|
|
9
|
+
"noVersionFound": "No version found",
|
|
10
|
+
"spaVersion": "SPA Version",
|
|
11
|
+
"systemInfo": "System Info",
|
|
12
|
+
"version": "Version"
|
|
3
13
|
}
|
package/tsconfig.json
CHANGED
package/dist/127.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"use strict";(globalThis.webpackChunk_kenyaemr_esm_version_app=globalThis.webpackChunk_kenyaemr_esm_version_app||[]).push([["127"],{1066:function(e){e.exports=JSON.parse('{"systemInfo":"System Info"}')}}]);
|