@kenyaemr/esm-admin-app 5.4.4-pre.142 → 5.4.4-pre.15

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 (279) hide show
  1. package/package.json +1 -1
  2. package/src/components/confirm-modal/confirmation-operation.test.tsx +8 -19
  3. package/src/components/dashboard/dashboard.component.tsx +4 -1
  4. package/src/components/empty-state/empty-state-log.test.tsx +3 -4
  5. package/src/components/facility-setup/constant/index.ts +3 -0
  6. package/src/components/facility-setup/facility-info.component.tsx +176 -106
  7. package/src/components/facility-setup/facility-info.scss +39 -1
  8. package/src/components/facility-setup/facility-setup.component.tsx +2 -2
  9. package/src/components/facility-setup/type/index.ts +61 -0
  10. package/src/components/facility-setup/useFacilityRegistry.ts +29 -0
  11. package/src/components/global-property/dashboard/global-property-dashboard.component.tsx +23 -0
  12. package/src/components/global-property/dashboard/global-property-dashboard.scss +6 -0
  13. package/src/components/global-property/hooks/useGlobalProperty.ts +64 -0
  14. package/src/components/global-property/index.ts +14 -0
  15. package/src/components/global-property/modal/delete-global-property-modal.component.tsx +71 -0
  16. package/src/components/global-property/modal/delete-global-property-modal.test.tsx +131 -0
  17. package/src/components/global-property/table/global-property-table.component.tsx +249 -0
  18. package/src/components/global-property/table/global-property-table.scss +34 -0
  19. package/src/components/global-property/table/global-property-table.test.tsx +198 -0
  20. package/src/components/global-property/workspace/global-property-form-schema.ts +32 -0
  21. package/src/components/global-property/workspace/global-property.workspace.scss +40 -0
  22. package/src/components/global-property/workspace/global-property.workspace.test.tsx +172 -0
  23. package/src/components/global-property/workspace/global-property.workspace.tsx +260 -0
  24. package/src/components/hook/healthWorkerRegistry.ts +78 -0
  25. package/src/components/hook/useProfessionalRegistryEnums.ts +59 -0
  26. package/src/components/logs-table/operation-log-table.component.tsx +87 -75
  27. package/src/components/logs-table/operation-log.test.tsx +134 -28
  28. package/src/components/modal/hwr-confirmation.modal.scss +80 -4
  29. package/src/components/modal/hwr-confirmation.modal.tsx +118 -128
  30. package/src/components/modal/hwr-sync.modal.tsx +194 -106
  31. package/src/components/users/manage-users/user-details/user-detail.scss +167 -39
  32. package/src/components/users/manage-users/user-details/user-details.component.tsx +130 -122
  33. package/src/components/users/manage-users/user-management.workspace.scss +233 -95
  34. package/src/components/users/manage-users/user-management.workspace.tsx +481 -375
  35. package/src/components/users/userManagementFormSchema.tsx +17 -8
  36. package/src/config-schema.ts +48 -68
  37. package/src/index.ts +9 -1
  38. package/src/root.component.tsx +2 -0
  39. package/src/routes.json +20 -1
  40. package/src/types/index.ts +11 -2
  41. package/translations/am.json +175 -10
  42. package/translations/en.json +108 -40
  43. package/translations/fr.json +171 -101
  44. package/translations/sw.json +312 -147
  45. package/.turbo/turbo-build.log +0 -6
  46. package/dist/1074.js +0 -1
  47. package/dist/1074.js.map +0 -1
  48. package/dist/12.js +0 -17
  49. package/dist/12.js.map +0 -1
  50. package/dist/1201.js +0 -1
  51. package/dist/1201.js.map +0 -1
  52. package/dist/1242.js +0 -1
  53. package/dist/1242.js.map +0 -1
  54. package/dist/1311.js +0 -1
  55. package/dist/1311.js.map +0 -1
  56. package/dist/1462.js +0 -1
  57. package/dist/1462.js.map +0 -1
  58. package/dist/1469.js +0 -1
  59. package/dist/1469.js.map +0 -1
  60. package/dist/1506.js +0 -13
  61. package/dist/1506.js.map +0 -1
  62. package/dist/1718.js +0 -1
  63. package/dist/1718.js.map +0 -1
  64. package/dist/1722.js +0 -1
  65. package/dist/1722.js.map +0 -1
  66. package/dist/1772.js +0 -1
  67. package/dist/1772.js.map +0 -1
  68. package/dist/1889.js +0 -1
  69. package/dist/1889.js.map +0 -1
  70. package/dist/1972.js +0 -1
  71. package/dist/1972.js.map +0 -1
  72. package/dist/1990.js +0 -1
  73. package/dist/1990.js.map +0 -1
  74. package/dist/2016.js +0 -1
  75. package/dist/2016.js.map +0 -1
  76. package/dist/2080.js +0 -1
  77. package/dist/2080.js.map +0 -1
  78. package/dist/2096.js +0 -1
  79. package/dist/2096.js.map +0 -1
  80. package/dist/2153.js +0 -1
  81. package/dist/2153.js.map +0 -1
  82. package/dist/216.js +0 -1
  83. package/dist/216.js.map +0 -1
  84. package/dist/2270.js +0 -1
  85. package/dist/2270.js.map +0 -1
  86. package/dist/2294.js +0 -1
  87. package/dist/2294.js.map +0 -1
  88. package/dist/2345.js +0 -1
  89. package/dist/2345.js.map +0 -1
  90. package/dist/2402.js +0 -1
  91. package/dist/2402.js.map +0 -1
  92. package/dist/2500.js +0 -1
  93. package/dist/2500.js.map +0 -1
  94. package/dist/251.js +0 -1
  95. package/dist/251.js.map +0 -1
  96. package/dist/257.js +0 -1
  97. package/dist/257.js.map +0 -1
  98. package/dist/2586.js +0 -1
  99. package/dist/2586.js.map +0 -1
  100. package/dist/2625.js +0 -1
  101. package/dist/2625.js.map +0 -1
  102. package/dist/2652.js +0 -1
  103. package/dist/2652.js.map +0 -1
  104. package/dist/2685.js +0 -1
  105. package/dist/2685.js.map +0 -1
  106. package/dist/2948.js +0 -1
  107. package/dist/2948.js.map +0 -1
  108. package/dist/3089.js +0 -1
  109. package/dist/3089.js.map +0 -1
  110. package/dist/3190.js +0 -1
  111. package/dist/3190.js.map +0 -1
  112. package/dist/3224.js +0 -1
  113. package/dist/3224.js.map +0 -1
  114. package/dist/3366.js +0 -1
  115. package/dist/3366.js.map +0 -1
  116. package/dist/349.js +0 -1
  117. package/dist/349.js.map +0 -1
  118. package/dist/3548.js +0 -1
  119. package/dist/3548.js.map +0 -1
  120. package/dist/3571.js +0 -1
  121. package/dist/3571.js.map +0 -1
  122. package/dist/3691.js +0 -1
  123. package/dist/3691.js.map +0 -1
  124. package/dist/3775.js +0 -1
  125. package/dist/3775.js.map +0 -1
  126. package/dist/3816.js +0 -1
  127. package/dist/3816.js.map +0 -1
  128. package/dist/3852.js +0 -1
  129. package/dist/3852.js.map +0 -1
  130. package/dist/3906.js +0 -1
  131. package/dist/3906.js.map +0 -1
  132. package/dist/3963.js +0 -1
  133. package/dist/3963.js.map +0 -1
  134. package/dist/405.js +0 -1
  135. package/dist/405.js.map +0 -1
  136. package/dist/4296.js +0 -1
  137. package/dist/4296.js.map +0 -1
  138. package/dist/4337.js +0 -1
  139. package/dist/4337.js.map +0 -1
  140. package/dist/4735.js +0 -1
  141. package/dist/4735.js.map +0 -1
  142. package/dist/4744.js +0 -1
  143. package/dist/4744.js.map +0 -1
  144. package/dist/4813.js +0 -2
  145. package/dist/4813.js.map +0 -1
  146. package/dist/4858.js +0 -1
  147. package/dist/4858.js.map +0 -1
  148. package/dist/487.js +0 -1
  149. package/dist/487.js.map +0 -1
  150. package/dist/4970.js +0 -1
  151. package/dist/4970.js.map +0 -1
  152. package/dist/5202.js +0 -1
  153. package/dist/5202.js.map +0 -1
  154. package/dist/5294.js +0 -1
  155. package/dist/5294.js.map +0 -1
  156. package/dist/545.js +0 -1
  157. package/dist/545.js.map +0 -1
  158. package/dist/552.js +0 -1
  159. package/dist/552.js.map +0 -1
  160. package/dist/5592.js +0 -1
  161. package/dist/5592.js.map +0 -1
  162. package/dist/5669.js +0 -1
  163. package/dist/5669.js.map +0 -1
  164. package/dist/5884.js +0 -1
  165. package/dist/5884.js.map +0 -1
  166. package/dist/5940.js +0 -1
  167. package/dist/5940.js.map +0 -1
  168. package/dist/6092.js +0 -1
  169. package/dist/6092.js.map +0 -1
  170. package/dist/6155.js +0 -1
  171. package/dist/6155.js.map +0 -1
  172. package/dist/6178.js +0 -1
  173. package/dist/6178.js.map +0 -1
  174. package/dist/6399.js +0 -1
  175. package/dist/6399.js.map +0 -1
  176. package/dist/6456.js +0 -1
  177. package/dist/6466.js +0 -3
  178. package/dist/6466.js.map +0 -1
  179. package/dist/6492.js +0 -1
  180. package/dist/6492.js.map +0 -1
  181. package/dist/6676.js +0 -1
  182. package/dist/6676.js.map +0 -1
  183. package/dist/6800.js +0 -1
  184. package/dist/6800.js.map +0 -1
  185. package/dist/6976.js +0 -1
  186. package/dist/6976.js.map +0 -1
  187. package/dist/7005.js +0 -1
  188. package/dist/7005.js.map +0 -1
  189. package/dist/7201.js +0 -1
  190. package/dist/7201.js.map +0 -1
  191. package/dist/7210.js +0 -1
  192. package/dist/7210.js.map +0 -1
  193. package/dist/7234.js +0 -1
  194. package/dist/7234.js.map +0 -1
  195. package/dist/7261.js +0 -1
  196. package/dist/7261.js.map +0 -1
  197. package/dist/7326.js +0 -1
  198. package/dist/7463.js +0 -1
  199. package/dist/7463.js.map +0 -1
  200. package/dist/7528.js +0 -1
  201. package/dist/7528.js.map +0 -1
  202. package/dist/7584.js +0 -1
  203. package/dist/7584.js.map +0 -1
  204. package/dist/7607.js +0 -1
  205. package/dist/7717.js +0 -1
  206. package/dist/7717.js.map +0 -1
  207. package/dist/7737.js +0 -1
  208. package/dist/7737.js.map +0 -1
  209. package/dist/7739.js +0 -1
  210. package/dist/7739.js.map +0 -1
  211. package/dist/7765.js +0 -1
  212. package/dist/7765.js.map +0 -1
  213. package/dist/7820.js +0 -1
  214. package/dist/7820.js.map +0 -1
  215. package/dist/7844.js +0 -1
  216. package/dist/7844.js.map +0 -1
  217. package/dist/7866.js +0 -1
  218. package/dist/7866.js.map +0 -1
  219. package/dist/7916.js +0 -1
  220. package/dist/7916.js.map +0 -1
  221. package/dist/7971.js +0 -1
  222. package/dist/7971.js.map +0 -1
  223. package/dist/8159.js +0 -7
  224. package/dist/8159.js.map +0 -1
  225. package/dist/8244.js +0 -1
  226. package/dist/8244.js.map +0 -1
  227. package/dist/8262.js +0 -1
  228. package/dist/8262.js.map +0 -1
  229. package/dist/834.js +0 -1
  230. package/dist/834.js.map +0 -1
  231. package/dist/8376.js +0 -1
  232. package/dist/8376.js.map +0 -1
  233. package/dist/845.js +0 -1
  234. package/dist/845.js.map +0 -1
  235. package/dist/8546.js +0 -1
  236. package/dist/8546.js.map +0 -1
  237. package/dist/8570.js +0 -1
  238. package/dist/8570.js.map +0 -1
  239. package/dist/87.js +0 -1
  240. package/dist/87.js.map +0 -1
  241. package/dist/8727.js +0 -1
  242. package/dist/8828.js +0 -1
  243. package/dist/8828.js.map +0 -1
  244. package/dist/8860.js +0 -1
  245. package/dist/8860.js.map +0 -1
  246. package/dist/9036.js +0 -1
  247. package/dist/9036.js.map +0 -1
  248. package/dist/9124.js +0 -1
  249. package/dist/9124.js.map +0 -1
  250. package/dist/9182.js +0 -1
  251. package/dist/921.js +0 -1
  252. package/dist/921.js.map +0 -1
  253. package/dist/9404.js +0 -1
  254. package/dist/9404.js.map +0 -1
  255. package/dist/9406.js +0 -1
  256. package/dist/9406.js.map +0 -1
  257. package/dist/9446.js +0 -1
  258. package/dist/9446.js.map +0 -1
  259. package/dist/9449.js +0 -1
  260. package/dist/9449.js.map +0 -1
  261. package/dist/9566.js +0 -5
  262. package/dist/9566.js.map +0 -1
  263. package/dist/9641.js +0 -1
  264. package/dist/9641.js.map +0 -1
  265. package/dist/9711.js +0 -1
  266. package/dist/9711.js.map +0 -1
  267. package/dist/9801.js +0 -1
  268. package/dist/9801.js.map +0 -1
  269. package/dist/9835.js +0 -11
  270. package/dist/9835.js.map +0 -1
  271. package/dist/kenyaemr-esm-admin-app.js +0 -5
  272. package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +0 -3173
  273. package/dist/kenyaemr-esm-admin-app.js.map +0 -1
  274. package/dist/main.js +0 -5
  275. package/dist/main.js.map +0 -1
  276. package/dist/routes.json +0 -1
  277. package/src/components/facility-setup/facility-setup.resource.tsx +0 -7
  278. package/src/components/hook/healthWorkerAdapter.ts +0 -213
  279. package/src/components/hook/useFacilityInfo.tsx +0 -37
@@ -0,0 +1,260 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import {
3
+ ResponsiveWrapper,
4
+ Workspace2,
5
+ showSnackbar,
6
+ useLayoutType,
7
+ type Workspace2DefinitionProps,
8
+ } from '@openmrs/esm-framework';
9
+ import { useTranslation } from 'react-i18next';
10
+ import { Controller, useForm } from 'react-hook-form';
11
+ import { Button, ButtonSet, ComboBox, Form, FormGroup, InlineLoading, Stack, TextArea, TextInput } from '@carbon/react';
12
+ import { zodResolver } from '@hookform/resolvers/zod';
13
+ import classNames from 'classnames';
14
+
15
+ import styles from './global-property.workspace.scss';
16
+ import { type SystemSettings } from '../../../types';
17
+ import { saveOrUpdateGlobalProperty } from '../hooks/useGlobalProperty';
18
+ import {
19
+ createGlobalPropertyFormSchema,
20
+ openmrsCustomDatatypes,
21
+ type GlobalPropertyFormType,
22
+ } from './global-property-form-schema';
23
+
24
+ type GlobalPropertyWorkspaceProps = {
25
+ systemSetting?: SystemSettings | null;
26
+ mutateGlobalProperty: () => void;
27
+ };
28
+
29
+ const GlobalPropertyWorkspace: React.FC<Workspace2DefinitionProps<GlobalPropertyWorkspaceProps, {}, {}>> = ({
30
+ closeWorkspace,
31
+ workspaceProps: { systemSetting, mutateGlobalProperty },
32
+ }) => {
33
+ const { t } = useTranslation();
34
+ const isTablet = useLayoutType() === 'tablet';
35
+ const isEditMode = Boolean(systemSetting?.uuid);
36
+ const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
37
+
38
+ const {
39
+ handleSubmit,
40
+ control,
41
+ formState: { isSubmitting, isDirty, errors },
42
+ } = useForm<GlobalPropertyFormType>({
43
+ resolver: zodResolver(createGlobalPropertyFormSchema(t)),
44
+ defaultValues: {
45
+ property: systemSetting?.property ?? '',
46
+ description: systemSetting?.description ?? '',
47
+ datatypeClassname: systemSetting?.datatypeClassname ?? '',
48
+ datatypeConfig: systemSetting?.datatypeConfig ?? '',
49
+ preferredHandlerClassname: systemSetting?.preferredHandlerClassname ?? '',
50
+ handlerConfig: systemSetting?.handlerConfig ?? '',
51
+ value: systemSetting?.value ?? '',
52
+ },
53
+ });
54
+
55
+ useEffect(() => {
56
+ setHasUnsavedChanges(isDirty);
57
+ }, [isDirty]);
58
+
59
+ const onSubmit = async (data: GlobalPropertyFormType) => {
60
+ const payload = {
61
+ property: data.property,
62
+ value: data.value,
63
+ description: data.description,
64
+ datatypeClassname: data.datatypeClassname,
65
+ datatypeConfig: data.datatypeConfig,
66
+ preferredHandlerClassname: data.preferredHandlerClassname,
67
+ handlerConfig: data.handlerConfig,
68
+ };
69
+ try {
70
+ await saveOrUpdateGlobalProperty(payload, systemSetting?.uuid);
71
+
72
+ showSnackbar({
73
+ title: t('success', 'Success'),
74
+ kind: 'success',
75
+ subtitle: isEditMode
76
+ ? t('gpUpdated', 'Global property {{property}} was updated successfully.', { property: data.property })
77
+ : t('gpCreated', 'Global property {{property}} was created successfully.', { property: data.property }),
78
+ });
79
+ mutateGlobalProperty();
80
+ closeWorkspace({ discardUnsavedChanges: true });
81
+ } catch (error: any) {
82
+ showSnackbar({
83
+ title: t('error', 'Error'),
84
+ kind: 'error',
85
+ subtitle: error?.message ?? t('gpSaveError', 'Error saving global property'),
86
+ });
87
+ }
88
+ };
89
+
90
+ const title = isEditMode
91
+ ? t('editGlobalProperty', 'Edit global property')
92
+ : t('addGlobalProperty', 'Add global property');
93
+
94
+ return (
95
+ <Workspace2 title={title} hasUnsavedChanges={hasUnsavedChanges}>
96
+ <Form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
97
+ <div className={styles.formContainer}>
98
+ <Stack gap={4}>
99
+ <ResponsiveWrapper>
100
+ <FormGroup legendText="">
101
+ <Controller
102
+ control={control}
103
+ name="property"
104
+ render={({ field }) => (
105
+ <TextInput
106
+ id="gpProperty"
107
+ labelText={t('property', 'Property (required)')}
108
+ placeholder={t('gpPropertyPlaceholder', 'e.g. setting.name')}
109
+ value={field.value}
110
+ onChange={field.onChange}
111
+ invalid={!!errors.property}
112
+ invalidText={errors.property?.message}
113
+ disabled={isEditMode}
114
+ />
115
+ )}
116
+ />
117
+ </FormGroup>
118
+ </ResponsiveWrapper>
119
+
120
+ <ResponsiveWrapper>
121
+ <FormGroup legendText="">
122
+ <Controller
123
+ control={control}
124
+ name="value"
125
+ render={({ field }) => (
126
+ <TextArea
127
+ id="gpValue"
128
+ labelText={t('value', 'Value (required)')}
129
+ placeholder={t('gpValuePlaceholder', 'Enter value')}
130
+ value={field.value}
131
+ onChange={field.onChange}
132
+ invalid={!!errors.value}
133
+ invalidText={errors.value?.message}
134
+ rows={3}
135
+ />
136
+ )}
137
+ />
138
+ </FormGroup>
139
+ </ResponsiveWrapper>
140
+
141
+ <ResponsiveWrapper>
142
+ <FormGroup legendText="">
143
+ <Controller
144
+ control={control}
145
+ name="description"
146
+ render={({ field }) => (
147
+ <TextArea
148
+ id="gpDescription"
149
+ labelText={t('description', 'Description')}
150
+ placeholder={t('gpDescriptionPlaceholder', 'Optional description')}
151
+ value={field.value ?? ''}
152
+ onChange={field.onChange}
153
+ rows={2}
154
+ />
155
+ )}
156
+ />
157
+ </FormGroup>
158
+ </ResponsiveWrapper>
159
+
160
+ <ResponsiveWrapper>
161
+ <FormGroup legendText="">
162
+ <Controller
163
+ control={control}
164
+ name="datatypeClassname"
165
+ render={({ field }) => (
166
+ <ComboBox
167
+ id="datatypeClassname"
168
+ invalidText={errors.datatypeClassname?.message}
169
+ items={openmrsCustomDatatypes}
170
+ onChange={function Zye() {}}
171
+ titleText={t('datatypeClassname', 'Datatype classname')}
172
+ typeahead
173
+ placeholder={t('selectDatatypeClassname', 'Select datatype classname')}
174
+ />
175
+ )}
176
+ />
177
+ </FormGroup>
178
+ </ResponsiveWrapper>
179
+
180
+ <ResponsiveWrapper>
181
+ <FormGroup legendText="">
182
+ <Controller
183
+ control={control}
184
+ name="datatypeConfig"
185
+ render={({ field }) => (
186
+ <TextInput
187
+ id="gpDatatypeConfig"
188
+ labelText={t('datatypeConfig', 'Datatype config')}
189
+ placeholder={t('gpDatatypeConfigPlaceholder', 'Optional datatype configuration')}
190
+ value={field.value ?? ''}
191
+ onChange={field.onChange}
192
+ />
193
+ )}
194
+ />
195
+ </FormGroup>
196
+ </ResponsiveWrapper>
197
+
198
+ <ResponsiveWrapper>
199
+ <FormGroup legendText="">
200
+ <Controller
201
+ control={control}
202
+ name="preferredHandlerClassname"
203
+ render={({ field }) => (
204
+ <TextInput
205
+ id="gpPreferredHandlerClassname"
206
+ labelText={t('preferredHandlerClassname', 'Preferred handler classname')}
207
+ placeholder={t('gpPreferredHandlerPlaceholder', 'Optional preferred handler classname')}
208
+ value={field.value ?? ''}
209
+ onChange={field.onChange}
210
+ />
211
+ )}
212
+ />
213
+ </FormGroup>
214
+ </ResponsiveWrapper>
215
+
216
+ <ResponsiveWrapper>
217
+ <FormGroup legendText="">
218
+ <Controller
219
+ control={control}
220
+ name="handlerConfig"
221
+ render={({ field }) => (
222
+ <TextInput
223
+ id="gpHandlerConfig"
224
+ labelText={t('handlerConfig', 'Handler config')}
225
+ placeholder={t('gpHandlerConfigPlaceholder', 'Optional handler configuration')}
226
+ value={field.value ?? ''}
227
+ onChange={field.onChange}
228
+ />
229
+ )}
230
+ />
231
+ </FormGroup>
232
+ </ResponsiveWrapper>
233
+ </Stack>
234
+ </div>
235
+
236
+ <ButtonSet
237
+ className={classNames({
238
+ [styles.tablet]: isTablet,
239
+ [styles.desktop]: !isTablet,
240
+ })}>
241
+ <Button className={styles.buttonContainer} kind="secondary" onClick={() => closeWorkspace()}>
242
+ {t('cancel', 'Cancel')}
243
+ </Button>
244
+ <Button className={styles.buttonContainer} disabled={isSubmitting || !isDirty} kind="primary" type="submit">
245
+ {isSubmitting ? (
246
+ <span className={styles.inlineLoading}>
247
+ {t('submitting', 'Submitting...')}
248
+ <InlineLoading status="active" iconDescription="Loading" />
249
+ </span>
250
+ ) : (
251
+ t('saveAndClose', 'Save & close')
252
+ )}
253
+ </Button>
254
+ </ButtonSet>
255
+ </Form>
256
+ </Workspace2>
257
+ );
258
+ };
259
+
260
+ export default GlobalPropertyWorkspace;
@@ -0,0 +1,78 @@
1
+ import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+ import { HWR_API_NO_CREDENTIALS, PROVIDER_NOT_FOUND, RESOURCE_NOT_FOUND, UNKNOWN } from '../../constants';
4
+
5
+ export interface ProfessionalRegistryResponse {
6
+ success: boolean;
7
+ regulator: string;
8
+ identification_type: string;
9
+ identification_number: string;
10
+ professional: {
11
+ membership: {
12
+ id: string;
13
+ full_name: string;
14
+ gender: string;
15
+ first_name: string;
16
+ middle_name: string;
17
+ last_name: string;
18
+ registration_id: string;
19
+ external_reference_id: string;
20
+ licensing_body: string;
21
+ specialty: string;
22
+ is_active: number;
23
+ };
24
+ licenses?: Array<{
25
+ id: string;
26
+ external_reference_id: string;
27
+ license_type: string;
28
+ license_start: string;
29
+ license_end: string;
30
+ }>;
31
+ professional_details?: {
32
+ professional_cadre?: string;
33
+ practice_type?: string;
34
+ educational_qualifications?: string;
35
+ };
36
+ contacts?: {
37
+ phone?: string;
38
+ email?: string;
39
+ postal_address?: string;
40
+ };
41
+ identifiers?: {
42
+ identification_type?: string;
43
+ identification_number?: string;
44
+ };
45
+ };
46
+ }
47
+
48
+ export const searchHealthCareWork = async (
49
+ identifierType: string,
50
+ identifierNumber: string,
51
+ regulator: string,
52
+ ): Promise<ProfessionalRegistryResponse> => {
53
+ const url = `${restBaseUrl}/virtualclaims/professional-registry?${new URLSearchParams({
54
+ identification_number: identifierNumber,
55
+ identification_type: identifierType,
56
+ regulator,
57
+ }).toString()}`;
58
+
59
+ try {
60
+ const response = await openmrsFetch<ProfessionalRegistryResponse>(url);
61
+ if (!response.data?.success || !response.data?.professional) {
62
+ throw new Error(PROVIDER_NOT_FOUND);
63
+ }
64
+ return response.data;
65
+ } catch (err: any) {
66
+ if (err?.message === PROVIDER_NOT_FOUND) {
67
+ throw err;
68
+ }
69
+ const status = err?.response?.status;
70
+ if (status === 401) {
71
+ throw new Error(HWR_API_NO_CREDENTIALS);
72
+ }
73
+ if (status === 404) {
74
+ throw new Error(RESOURCE_NOT_FOUND);
75
+ }
76
+ throw new Error(UNKNOWN);
77
+ }
78
+ };
@@ -0,0 +1,59 @@
1
+ import { FetchResponse, openmrsFetch, restBaseUrl, useSession } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+
4
+ export type EnumEntry = {
5
+ code: string;
6
+ label: string;
7
+ };
8
+
9
+ type RegulatorsResponse = {
10
+ count: number;
11
+ regulators: Array<EnumEntry>;
12
+ };
13
+
14
+ type IdentificationTypesResponse = {
15
+ count: number;
16
+ identification_types: Array<EnumEntry>;
17
+ };
18
+
19
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
20
+
21
+ export const useProfessionalRegistryRegulators = () => {
22
+ const { authenticated } = useSession();
23
+ const url = `${restBaseUrl}/virtualclaims/professional-registry/regulators`;
24
+
25
+ const { data, error, isLoading } = useSWR<FetchResponse<RegulatorsResponse>>(
26
+ authenticated ? url : null,
27
+ openmrsFetch,
28
+ {
29
+ revalidateOnFocus: false,
30
+ dedupingInterval: ONE_DAY_MS,
31
+ },
32
+ );
33
+
34
+ return {
35
+ regulators: data?.data?.regulators ?? [],
36
+ isLoading,
37
+ error,
38
+ };
39
+ };
40
+
41
+ export const useProfessionalRegistryIdentificationTypes = () => {
42
+ const { authenticated } = useSession();
43
+ const url = `${restBaseUrl}/virtualclaims/professional-registry/identification-types`;
44
+
45
+ const { data, error, isLoading } = useSWR<FetchResponse<IdentificationTypesResponse>>(
46
+ authenticated ? url : null,
47
+ openmrsFetch,
48
+ {
49
+ revalidateOnFocus: false,
50
+ dedupingInterval: ONE_DAY_MS,
51
+ },
52
+ );
53
+
54
+ return {
55
+ identificationTypes: data?.data?.identification_types ?? [],
56
+ isLoading,
57
+ error,
58
+ };
59
+ };
@@ -12,7 +12,6 @@ import {
12
12
  TableRow,
13
13
  Tag,
14
14
  DataTableSkeleton,
15
- InlineLoading,
16
15
  } from '@carbon/react';
17
16
  import { CardHeader, PatientChartPagination } from '@openmrs/esm-patient-common-lib';
18
17
  import { useLayoutType, usePagination, formatDate } from '@openmrs/esm-framework';
@@ -21,13 +20,14 @@ import { ETLResponse } from '../../types';
21
20
  import EmptyState from '../empty-state/empty-state-log.components';
22
21
 
23
22
  interface LogTableProps {
24
- logData: ETLResponse[];
23
+ logData: Array<ETLResponse>;
25
24
  isLoading: boolean;
26
25
  }
27
26
 
27
+ const PAGE_SIZE = 10;
28
+
28
29
  const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {
29
30
  const { t } = useTranslation();
30
- const pageSize = 10;
31
31
  const isTablet = useLayoutType() === 'tablet';
32
32
 
33
33
  const headers = [
@@ -37,82 +37,94 @@ const LogTable: React.FC<LogTableProps> = ({ logData, isLoading }) => {
37
37
  { header: t('completionStatus', 'Completion status'), key: 'status' },
38
38
  ];
39
39
 
40
- const rows = logData?.map((item, index) => ({
41
- id: index.toString(),
42
- script_name: item.script_name,
43
- start_time: item?.start_time ? formatDate(new Date(item?.start_time)) : '--',
44
- stop_time: item?.stop_time ? formatDate(new Date(item?.stop_time)) : '--',
45
- status: item.status,
46
- }));
40
+ const rows =
41
+ logData?.map((item, index) => ({
42
+ id: index.toString(),
43
+ script_name: item.script_name,
44
+ start_time: item?.start_time ? formatDate(new Date(item.start_time)) : '--',
45
+ stop_time: item?.stop_time ? formatDate(new Date(item.stop_time)) : '--',
46
+ status: item.status,
47
+ })) ?? [];
48
+
49
+ const { results: paginatedData, currentPage, goTo } = usePagination(rows, PAGE_SIZE);
50
+
51
+ const renderTable = ({ rows, headers, getHeaderProps, getTableProps }: any) => (
52
+ <TableContainer className={styles.tableContainer}>
53
+ <Table {...getTableProps()}>
54
+ <TableHead>
55
+ <TableRow>
56
+ {headers.map((header: any) => (
57
+ <TableHeader
58
+ className={classNames(styles.productiveHeading01, styles.text02)}
59
+ {...getHeaderProps({ header })}>
60
+ {header.header}
61
+ </TableHeader>
62
+ ))}
63
+ </TableRow>
64
+ </TableHead>
65
+ <TableBody>
66
+ {rows.map((row: any) => (
67
+ <TableRow key={row.id}>
68
+ {row.cells.map((cell: any) => (
69
+ <TableCell key={cell.id}>
70
+ {cell.info.header === 'status' ? (
71
+ <Tag size="md" type={cell.value === 'Success' ? 'green' : 'red'}>
72
+ {cell.value}
73
+ </Tag>
74
+ ) : (
75
+ cell.value
76
+ )}
77
+ </TableCell>
78
+ ))}
79
+ </TableRow>
80
+ ))}
81
+ </TableBody>
82
+ </Table>
83
+ </TableContainer>
84
+ );
85
+
86
+ const renderContent = () => {
87
+ if (isLoading && logData.length === 0) {
88
+ return (
89
+ <DataTableSkeleton
90
+ headers={headers}
91
+ aria-label="etl table"
92
+ showToolbar={false}
93
+ showHeader={false}
94
+ rowCount={4}
95
+ zebra
96
+ columnCount={3}
97
+ className={styles.dataTableSkeleton}
98
+ />
99
+ );
100
+ }
101
+
102
+ if (logData.length === 0) {
103
+ return <EmptyState subTitle={t('noRecordsFound', 'No ETL Operation logs found')} />;
104
+ }
47
105
 
48
- const { results: paginatedData, currentPage, goTo } = usePagination(rows, pageSize);
106
+ return (
107
+ <>
108
+ <DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
109
+ {renderTable}
110
+ </DataTable>
111
+ <PatientChartPagination
112
+ currentItems={paginatedData.length}
113
+ totalItems={rows.length}
114
+ onPageNumberChange={({ page }: { page: number }) => goTo(page)}
115
+ pageNumber={currentPage}
116
+ pageSize={PAGE_SIZE}
117
+ />
118
+ </>
119
+ );
120
+ };
49
121
 
50
122
  return (
51
123
  <div className={styles.table}>
52
- <CardHeader title={t('facilityInfo', 'Facility Info')} children={''} />
53
- <div className={styles.logTable}>
54
- {isLoading && logData.length === 0 ? (
55
- <DataTableSkeleton
56
- headers={headers}
57
- aria-label="etl table"
58
- showToolbar={false}
59
- showHeader={false}
60
- rowCount={4}
61
- zebra
62
- columnCount={3}
63
- className={styles.dataTableSkeleton}
64
- />
65
- ) : logData.length === 0 ? (
66
- <EmptyState subTitle={t('noRecordsFound', 'No ETL Operation logs found')} />
67
- ) : (
68
- <>
69
- <DataTable rows={paginatedData} headers={headers} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
70
- {({ rows, headers, getHeaderProps, getTableProps }) => (
71
- <TableContainer className={styles.tableContainer}>
72
- <Table {...getTableProps()}>
73
- <TableHead>
74
- <TableRow>
75
- {headers.map((header) => (
76
- <TableHeader
77
- key={header.key}
78
- className={classNames(styles.productiveHeading01, styles.text02)}
79
- {...getHeaderProps({ header })}>
80
- {header.header}
81
- </TableHeader>
82
- ))}
83
- </TableRow>
84
- </TableHead>
85
- <TableBody>
86
- {rows.map((row) => (
87
- <TableRow key={row.id}>
88
- {row.cells.map((cell) => (
89
- <TableCell key={cell.id}>
90
- {cell.info.header === 'status' ? (
91
- <Tag size="md" type={cell.value === 'Success' ? 'green' : 'red'}>
92
- {cell.value}
93
- </Tag>
94
- ) : (
95
- cell.value
96
- )}
97
- </TableCell>
98
- ))}
99
- </TableRow>
100
- ))}
101
- </TableBody>
102
- </Table>
103
- </TableContainer>
104
- )}
105
- </DataTable>
106
- <PatientChartPagination
107
- currentItems={paginatedData.length}
108
- totalItems={rows.length}
109
- onPageNumberChange={({ page }) => goTo(page)}
110
- pageNumber={currentPage}
111
- pageSize={pageSize}
112
- />
113
- </>
114
- )}
115
- </div>
124
+ <CardHeader title={t('facilityInfo', 'Facility Info')}>
125
+ <></>
126
+ </CardHeader>
127
+ <div className={styles.logTable}>{renderContent()}</div>
116
128
  </div>
117
129
  );
118
130
  };