@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.
Files changed (206) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/dist/1074.js +1 -0
  3. package/dist/1074.js.map +1 -0
  4. package/dist/1311.js +1 -0
  5. package/dist/1311.js.map +1 -0
  6. package/dist/1469.js +1 -0
  7. package/dist/1469.js.map +1 -0
  8. package/dist/1718.js +1 -0
  9. package/dist/1718.js.map +1 -0
  10. package/dist/1772.js +1 -0
  11. package/dist/1772.js.map +1 -0
  12. package/dist/1889.js +1 -0
  13. package/dist/1889.js.map +1 -0
  14. package/dist/1972.js +1 -0
  15. package/dist/1972.js.map +1 -0
  16. package/dist/1990.js +1 -0
  17. package/dist/1990.js.map +1 -0
  18. package/dist/2016.js +1 -0
  19. package/dist/2016.js.map +1 -0
  20. package/dist/2119.js +1 -0
  21. package/dist/2119.js.map +1 -0
  22. package/dist/2153.js +1 -0
  23. package/dist/2153.js.map +1 -0
  24. package/dist/216.js +1 -0
  25. package/dist/216.js.map +1 -0
  26. package/dist/2294.js +1 -0
  27. package/dist/2294.js.map +1 -0
  28. package/dist/2345.js +1 -0
  29. package/dist/2345.js.map +1 -0
  30. package/dist/2500.js +1 -0
  31. package/dist/2500.js.map +1 -0
  32. package/dist/251.js +1 -0
  33. package/dist/251.js.map +1 -0
  34. package/dist/257.js +1 -0
  35. package/dist/257.js.map +1 -0
  36. package/dist/2586.js +1 -0
  37. package/dist/2586.js.map +1 -0
  38. package/dist/2625.js +1 -0
  39. package/dist/2625.js.map +1 -0
  40. package/dist/2685.js +1 -0
  41. package/dist/2685.js.map +1 -0
  42. package/dist/2948.js +1 -0
  43. package/dist/2948.js.map +1 -0
  44. package/dist/3089.js +1 -0
  45. package/dist/3089.js.map +1 -0
  46. package/dist/3190.js +1 -0
  47. package/dist/3190.js.map +1 -0
  48. package/dist/3548.js +1 -0
  49. package/dist/3548.js.map +1 -0
  50. package/dist/3816.js +1 -0
  51. package/dist/3816.js.map +1 -0
  52. package/dist/3906.js +1 -0
  53. package/dist/3906.js.map +1 -0
  54. package/dist/3963.js +1 -0
  55. package/dist/3963.js.map +1 -0
  56. package/dist/405.js +1 -0
  57. package/dist/405.js.map +1 -0
  58. package/dist/4296.js +1 -0
  59. package/dist/4296.js.map +1 -0
  60. package/dist/4368.js +1 -0
  61. package/dist/4368.js.map +1 -0
  62. package/dist/4537.js +1 -0
  63. package/dist/4537.js.map +1 -0
  64. package/dist/4735.js +1 -0
  65. package/dist/4735.js.map +1 -0
  66. package/dist/4744.js +1 -0
  67. package/dist/4744.js.map +1 -0
  68. package/dist/4858.js +1 -0
  69. package/dist/4858.js.map +1 -0
  70. package/dist/487.js +1 -0
  71. package/dist/487.js.map +1 -0
  72. package/dist/5128.js +1 -0
  73. package/dist/5128.js.map +1 -0
  74. package/dist/5192.js +17 -0
  75. package/dist/5192.js.map +1 -0
  76. package/dist/5202.js +1 -0
  77. package/dist/5202.js.map +1 -0
  78. package/dist/5592.js +1 -0
  79. package/dist/5592.js.map +1 -0
  80. package/dist/5669.js +1 -0
  81. package/dist/5669.js.map +1 -0
  82. package/dist/5795.js +1 -0
  83. package/dist/5795.js.map +1 -0
  84. package/dist/5880.js +1 -0
  85. package/dist/5880.js.map +1 -0
  86. package/dist/6178.js +1 -0
  87. package/dist/6178.js.map +1 -0
  88. package/dist/6373.js +13 -0
  89. package/dist/6373.js.map +1 -0
  90. package/dist/6456.js +1 -0
  91. package/dist/6492.js +1 -0
  92. package/dist/6492.js.map +1 -0
  93. package/dist/6495.js +1 -0
  94. package/dist/6495.js.map +1 -0
  95. package/dist/6561.js +1 -0
  96. package/dist/6561.js.map +1 -0
  97. package/dist/6800.js +1 -0
  98. package/dist/6800.js.map +1 -0
  99. package/dist/6962.js +1 -0
  100. package/dist/6962.js.map +1 -0
  101. package/dist/7005.js +1 -0
  102. package/dist/7005.js.map +1 -0
  103. package/dist/7201.js +1 -0
  104. package/dist/7201.js.map +1 -0
  105. package/dist/7234.js +1 -0
  106. package/dist/7234.js.map +1 -0
  107. package/dist/7261.js +1 -0
  108. package/dist/7261.js.map +1 -0
  109. package/dist/7326.js +1 -0
  110. package/dist/7463.js +1 -0
  111. package/dist/7463.js.map +1 -0
  112. package/dist/7497.js +1 -0
  113. package/dist/7497.js.map +1 -0
  114. package/dist/7607.js +1 -0
  115. package/dist/7626.js +1 -0
  116. package/dist/7626.js.map +1 -0
  117. package/dist/7701.js +1 -0
  118. package/dist/7701.js.map +1 -0
  119. package/dist/7717.js +1 -0
  120. package/dist/7717.js.map +1 -0
  121. package/dist/7739.js +1 -0
  122. package/dist/7739.js.map +1 -0
  123. package/dist/7765.js +1 -0
  124. package/dist/7765.js.map +1 -0
  125. package/dist/7971.js +1 -0
  126. package/dist/7971.js.map +1 -0
  127. package/dist/8159.js +7 -0
  128. package/dist/8159.js.map +1 -0
  129. package/dist/8244.js +1 -0
  130. package/dist/8244.js.map +1 -0
  131. package/dist/8252.js +4 -0
  132. package/dist/8252.js.map +1 -0
  133. package/dist/845.js +1 -0
  134. package/dist/845.js.map +1 -0
  135. package/dist/8570.js +1 -0
  136. package/dist/8570.js.map +1 -0
  137. package/dist/87.js +1 -0
  138. package/dist/87.js.map +1 -0
  139. package/dist/8727.js +1 -0
  140. package/dist/9124.js +1 -0
  141. package/dist/9124.js.map +1 -0
  142. package/dist/9182.js +1 -0
  143. package/dist/921.js +1 -0
  144. package/dist/921.js.map +1 -0
  145. package/dist/9404.js +1 -0
  146. package/dist/9404.js.map +1 -0
  147. package/dist/9446.js +1 -0
  148. package/dist/9446.js.map +1 -0
  149. package/dist/9449.js +1 -0
  150. package/dist/9449.js.map +1 -0
  151. package/dist/9566.js +5 -0
  152. package/dist/9566.js.map +1 -0
  153. package/dist/9641.js +1 -0
  154. package/dist/9641.js.map +1 -0
  155. package/dist/9801.js +1 -0
  156. package/dist/9801.js.map +1 -0
  157. package/dist/9835.js +11 -0
  158. package/dist/9835.js.map +1 -0
  159. package/dist/9900.js +1 -0
  160. package/dist/9900.js.map +1 -0
  161. package/dist/main.js +5 -5
  162. package/dist/main.js.map +1 -1
  163. package/dist/openmrs-esm-patient-verification-app.js +5 -5
  164. package/dist/openmrs-esm-patient-verification-app.js.buildmanifest.json +1917 -111
  165. package/dist/openmrs-esm-patient-verification-app.js.map +1 -1
  166. package/dist/routes.json +1 -1
  167. package/package.json +5 -7
  168. package/rspack.config.js +1 -1
  169. package/src/about/about.component.tsx +195 -27
  170. package/src/about/about.scss +57 -9
  171. package/src/config-schema.ts +10 -1
  172. package/src/frontend-modules/frontend-modules.component.test.tsx +62 -0
  173. package/src/frontend-modules/frontend-modules.component.tsx +47 -24
  174. package/src/hooks/useModules.tsx +9 -0
  175. package/src/index.ts +2 -3
  176. package/src/release-version.js +8 -8
  177. package/src/types/index.ts +15 -0
  178. package/translations/am.json +11 -1
  179. package/translations/en.json +11 -1
  180. package/translations/sw.json +11 -1
  181. package/tsconfig.json +1 -1
  182. package/dist/127.js +0 -1
  183. package/dist/129.js +0 -1
  184. package/dist/129.js.map +0 -1
  185. package/dist/267.js +0 -1
  186. package/dist/267.js.map +0 -1
  187. package/dist/286.js +0 -1
  188. package/dist/286.js.map +0 -1
  189. package/dist/40.js +0 -1
  190. package/dist/424.js +0 -43
  191. package/dist/424.js.map +0 -1
  192. package/dist/466.js +0 -1
  193. package/dist/466.js.map +0 -1
  194. package/dist/478.js +0 -1
  195. package/dist/478.js.map +0 -1
  196. package/dist/501.js +0 -1
  197. package/dist/675.js +0 -1
  198. package/dist/675.js.map +0 -1
  199. package/dist/689.js +0 -1
  200. package/dist/689.js.map +0 -1
  201. package/dist/916.js +0 -1
  202. package/dist/94.js +0 -15
  203. package/dist/94.js.map +0 -1
  204. package/dist/998.js +0 -1
  205. package/dist/998.js.map +0 -1
  206. package/jest.config.js +0 -3
@@ -1,45 +1,213 @@
1
- import React, { useEffect } from '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 { modules, isLoading } = useModules();
12
- const kenyaEMR = modules.find(({ uuid }) => uuid === 'kenyaemr');
13
- const { title, container, aboutBody, aboutPage } = styles;
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
- <div className={title}>
21
- <div>
22
- <h3>Government of Kenya</h3>
23
- <h4>Ministry of Health</h4>
24
- </div>
25
- <img
26
- src="/openmrs/ms/uiframework/resource/kenyaemr/images/logos/moh.png"
27
- alt="court_of_arms"
28
- width="50"
29
- height="50"
30
- />
31
- </div>
32
- <div className={aboutBody}>
33
- <p>KenyaEMR Version</p>
34
- <p>{`v${kenyaEMR?.version}`}</p>
35
- <p>SPA Version</p>
36
- <p>{`v${VERSION.version}`}</p>
37
- <p>Build date time</p>
38
- <p>{formatDatetime(new Date(VERSION.buildDate), { mode: 'standard' })}</p>
39
- </div>
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
- <FrontendModule />
208
+ <div className={styles.frontendModules}>
209
+ <FrontendModule />
210
+ </div>
43
211
  </div>
44
212
  );
45
213
  };
@@ -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: 23rem;
26
+ width: 60rem;
27
+ padding: layout.$spacing-05;
25
28
  }
26
29
 
27
30
  .title {
28
31
  display: flex;
29
- align-items: center;
30
- padding-bottom: 0.25rem;
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('body-compact-02');
41
+ @include type.type-style('heading-compact-01');
42
+ text-align: center;
43
+ margin: 0 0 layout.$spacing-02;
38
44
  }
39
45
 
40
- h4 {
41
- @include type.type-style('body-compact-01');
46
+ img {
47
+ margin-bottom: layout.$spacing-03;
48
+ object-fit: contain;
42
49
  }
50
+ }
43
51
 
44
- img {
45
- margin-left: 0.25rem;
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
+ }
@@ -1 +1,10 @@
1
- export const configSchema = {};
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
- <StructuredListWrapper ariaLabel="Structured list">
15
- <StructuredListHead>
16
- <StructuredListRow head tabIndex={0}>
17
- <StructuredListCell head>Module name</StructuredListCell>
18
- <StructuredListCell head>Version</StructuredListCell>
19
- </StructuredListRow>
20
- </StructuredListHead>
21
- <StructuredListBody>
22
- {installedModules.map((module) => (
23
- <StructuredListRow tabIndex={0}>
24
- <StructuredListCell>{module.name}</StructuredListCell>
25
- <StructuredListCell>{module.version ?? 'No version found'} </StructuredListCell>
26
- </StructuredListRow>
27
- ))}
28
- </StructuredListBody>
29
- </StructuredListWrapper>
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
 
@@ -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, getSyncLifecycle } from '@openmrs/esm-framework';
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 = getSyncLifecycle(rootComponent, options);
13
+ export const about = getAsyncLifecycle(() => import('./root.component'), options);
15
14
 
16
15
  export function startupApp() {
17
16
  defineConfigSchema(moduleName, configSchema);
@@ -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": false,
6
- "raw": "v5.4.3-0-gbde73da",
7
- "hash": "gbde73da",
8
- "distance": 0,
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": "0-gbde73da",
25
- "semverString": "5.4.3",
26
- "version": "5.4.3",
27
- "buildDate": "2026-02-02T07:55:57.637Z"
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 */
@@ -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
+ }
@@ -1,3 +1,13 @@
1
1
  {
2
- "systemInfo": "System Info"
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
  }
@@ -1,3 +1,13 @@
1
1
  {
2
- "systemInfo": "System Info"
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
  }
@@ -1,3 +1,13 @@
1
1
  {
2
- "systemInfo": "System Info"
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
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "extends": "../../tsconfig.json",
3
3
  "include": ["src/**/*", "increment-version.js"],
4
- "exclude": ["src/**/*.test.tsx", "src/**/*.outdated.tsx"]
4
+ "exclude": ["src/**/*.test.ts", "src/**/*.test.tsx", "src/**/*.outdated.tsx"]
5
5
  }
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"}')}}]);