@kenyaemr/esm-admin-app 5.4.2-pre.2269 → 5.4.2-pre.2272
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 +15 -15
- package/dist/342.js +1 -0
- package/dist/342.js.map +1 -0
- package/dist/965.js +1 -0
- package/dist/965.js.map +1 -0
- package/dist/kenyaemr-esm-admin-app.js +1 -1
- package/dist/kenyaemr-esm-admin-app.js.buildmanifest.json +54 -54
- package/dist/kenyaemr-esm-admin-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/components/locations/auto-suggest/autosuggest.component.tsx +149 -0
- package/src/components/locations/auto-suggest/autosuggest.scss +61 -0
- package/src/components/locations/auto-suggest/location-autosuggest.component.tsx +94 -0
- package/src/components/locations/auto-suggest/location-autosuggest.scss +48 -0
- package/src/components/locations/common/results-tile.component.tsx +45 -0
- package/src/components/locations/common/results-tile.scss +64 -0
- package/src/components/locations/forms/add-location/add-location.workspace.scss +34 -0
- package/src/components/locations/forms/add-location/add-location.workspace.tsx +202 -0
- package/src/components/locations/forms/search-location/search-location.workspace.scss +79 -0
- package/src/components/locations/forms/search-location/search-location.workspace.tsx +215 -0
- package/src/components/locations/header/header.component.tsx +48 -0
- package/src/components/locations/header/header.scss +58 -0
- package/src/components/locations/helpers/index.ts +16 -0
- package/src/components/locations/home/home-locations.component.tsx +18 -0
- package/src/components/locations/home/home-locations.scss +8 -0
- package/src/components/locations/hooks/UseFacilityLocations.ts +12 -0
- package/src/components/locations/hooks/index.json +35 -0
- package/src/components/locations/hooks/useLocation.ts +24 -0
- package/src/components/locations/hooks/useLocationTags.ts +15 -0
- package/src/components/locations/tables/locations-table.component.tsx +243 -0
- package/src/components/locations/tables/locations-table.resource.ts +41 -0
- package/src/components/locations/tables/locations-table.scss +115 -0
- package/src/components/locations/types/index.ts +120 -0
- package/src/components/locations/utils/index.ts +5 -0
- package/src/index.ts +9 -0
- package/src/root.component.tsx +2 -0
- package/src/routes.json +27 -3
- package/dist/479.js +0 -1
- package/dist/479.js.map +0 -1
- package/dist/512.js +0 -1
- package/dist/512.js.map +0 -1
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { restBaseUrl, openmrsFetch } from '@openmrs/esm-framework';
|
|
2
|
+
import useSWR from 'swr';
|
|
3
|
+
|
|
4
|
+
export const saveLocation = async (locationPayload) => {
|
|
5
|
+
const url = `${restBaseUrl}/location`;
|
|
6
|
+
return await openmrsFetch(url, {
|
|
7
|
+
method: 'POST',
|
|
8
|
+
body: locationPayload,
|
|
9
|
+
headers: {
|
|
10
|
+
'Content-Type': 'application/json',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const editLocation = async (locationUuid, locationPayload) => {
|
|
16
|
+
const url = `${restBaseUrl}/location/${locationUuid}`;
|
|
17
|
+
return await openmrsFetch(url, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
body: locationPayload,
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import useSWR from 'swr';
|
|
3
|
+
import { type LocationTagsResponse } from '../types/index';
|
|
4
|
+
|
|
5
|
+
export const useLocationTags = () => {
|
|
6
|
+
const customPresentation = 'custom:(uuid,display,name,description)';
|
|
7
|
+
const url = `${restBaseUrl}/locationtag?v=${customPresentation}`;
|
|
8
|
+
const { isLoading, error, data } = useSWR<FetchResponse<LocationTagsResponse>>(url, openmrsFetch);
|
|
9
|
+
|
|
10
|
+
return {
|
|
11
|
+
isLoading,
|
|
12
|
+
error,
|
|
13
|
+
locationTagList: data?.data?.results || [],
|
|
14
|
+
};
|
|
15
|
+
};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import React, { useMemo, useState, useEffect } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
DataTable,
|
|
6
|
+
DataTableSkeleton,
|
|
7
|
+
InlineLoading,
|
|
8
|
+
Pagination,
|
|
9
|
+
Search,
|
|
10
|
+
Table,
|
|
11
|
+
TableBody,
|
|
12
|
+
TableCell,
|
|
13
|
+
TableContainer,
|
|
14
|
+
TableHead,
|
|
15
|
+
TableHeader,
|
|
16
|
+
TableRow,
|
|
17
|
+
Tile,
|
|
18
|
+
Tag,
|
|
19
|
+
} from '@carbon/react';
|
|
20
|
+
import { Add } from '@carbon/react/icons';
|
|
21
|
+
import { WorkspaceContainer, isDesktop as desktopLayout, launchWorkspace, useLayoutType } from '@openmrs/esm-framework';
|
|
22
|
+
import styles from './locations-table.scss';
|
|
23
|
+
import { CardHeader } from '@openmrs/esm-patient-common-lib';
|
|
24
|
+
import { useFacilitiesTagged } from './locations-table.resource';
|
|
25
|
+
import { useLocationTags } from '../hooks/useLocationTags';
|
|
26
|
+
|
|
27
|
+
const LocationsTable: React.FC = () => {
|
|
28
|
+
const { t } = useTranslation();
|
|
29
|
+
const layout = useLayoutType();
|
|
30
|
+
const isTablet = layout === 'tablet';
|
|
31
|
+
const isDesktop = desktopLayout(layout);
|
|
32
|
+
const [pageSize, setPageSize] = useState(10);
|
|
33
|
+
const [searchTerm, setSearchTerm] = useState('');
|
|
34
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
35
|
+
|
|
36
|
+
const { locationTagList, isLoading: tagsLoading, error: tagsError } = useLocationTags();
|
|
37
|
+
const { facilityList, isLoading: taggedLoading } = useFacilitiesTagged({ results: locationTagList });
|
|
38
|
+
|
|
39
|
+
const handleAddLocationWorkspace = () => {
|
|
40
|
+
launchWorkspace('add-location-workspace', {
|
|
41
|
+
workspaceTitle: t('addLocation', 'Add Location'),
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const handleSearchLocationWorkspace = () => {
|
|
46
|
+
launchWorkspace('search-location-workspace', {
|
|
47
|
+
workspaceTitle: t('tagLocation', 'Tag Location'),
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const tableHeaders = [
|
|
52
|
+
{
|
|
53
|
+
key: 'name',
|
|
54
|
+
header: t('locationName', 'Location Name'),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: 'description',
|
|
58
|
+
header: t('description', 'Description'),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
key: 'tags',
|
|
62
|
+
header: t('tags', 'Tags'),
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
const getLocationTags = (resource) => {
|
|
67
|
+
if (!resource?.meta?.tag) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return resource.meta.tag.filter((tag) => tag.system && tag.system.includes('location-tag')).map((tag) => tag.code);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const rows = useMemo(() => {
|
|
75
|
+
const uniqueFacilities = new Map();
|
|
76
|
+
|
|
77
|
+
facilityList.forEach((facility) => {
|
|
78
|
+
const resource = facility.resource;
|
|
79
|
+
const uuid = resource?.id;
|
|
80
|
+
|
|
81
|
+
if (uuid && !uniqueFacilities.has(uuid)) {
|
|
82
|
+
uniqueFacilities.set(uuid, facility);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
return Array.from(uniqueFacilities.values()).map((facility) => {
|
|
87
|
+
const resource = facility.resource;
|
|
88
|
+
const tags = getLocationTags(resource);
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
id: resource?.id,
|
|
92
|
+
name: resource?.name || resource?.partOf?.display || '--',
|
|
93
|
+
description: resource?.description || '--',
|
|
94
|
+
tags: tags.length > 0 ? tags.join(', ') : '--',
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
}, [facilityList]);
|
|
98
|
+
|
|
99
|
+
const filteredRows = useMemo(() => {
|
|
100
|
+
if (!searchTerm) {
|
|
101
|
+
return rows;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return rows.filter(
|
|
105
|
+
(row) =>
|
|
106
|
+
row.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
107
|
+
row.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
|
|
108
|
+
row.tags.toLowerCase().includes(searchTerm.toLowerCase()),
|
|
109
|
+
);
|
|
110
|
+
}, [rows, searchTerm]);
|
|
111
|
+
|
|
112
|
+
const paginatedRows = useMemo(() => {
|
|
113
|
+
const startIndex = (currentPage - 1) * pageSize;
|
|
114
|
+
const endIndex = startIndex + pageSize;
|
|
115
|
+
return filteredRows.slice(startIndex, endIndex);
|
|
116
|
+
}, [filteredRows, currentPage, pageSize]);
|
|
117
|
+
|
|
118
|
+
if (tagsLoading || (taggedLoading && !facilityList.length)) {
|
|
119
|
+
return (
|
|
120
|
+
<>
|
|
121
|
+
<div className={styles.widgetCard}>
|
|
122
|
+
<DataTableSkeleton role="progressbar" compact={isDesktop} zebra />
|
|
123
|
+
</div>
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<>
|
|
130
|
+
<div className={styles.widgetCard}>
|
|
131
|
+
<CardHeader title={t('locations', 'Locations')}>
|
|
132
|
+
<span className={styles.backgroundDataFetchingIndicator}>
|
|
133
|
+
<span>{tagsLoading ? <InlineLoading /> : null}</span>
|
|
134
|
+
</span>
|
|
135
|
+
<div className={styles.headerActions}>
|
|
136
|
+
<>
|
|
137
|
+
<div className={styles.filterContainer}>
|
|
138
|
+
<Search
|
|
139
|
+
onChange={(e) => {
|
|
140
|
+
setSearchTerm(e.target.value);
|
|
141
|
+
setCurrentPage(1);
|
|
142
|
+
}}
|
|
143
|
+
placeholder={t('search', 'Search location')}
|
|
144
|
+
value={searchTerm}
|
|
145
|
+
labelText={t('search', 'Search location')}
|
|
146
|
+
/>
|
|
147
|
+
</div>
|
|
148
|
+
<Button
|
|
149
|
+
kind="ghost"
|
|
150
|
+
renderIcon={(props) => <Add size={16} {...props} />}
|
|
151
|
+
onClick={handleAddLocationWorkspace}>
|
|
152
|
+
{t('addLocation', 'Add Location')}
|
|
153
|
+
</Button>
|
|
154
|
+
<Button
|
|
155
|
+
kind="ghost"
|
|
156
|
+
renderIcon={(props) => <Add size={16} {...props} />}
|
|
157
|
+
onClick={handleSearchLocationWorkspace}>
|
|
158
|
+
{t('tagLocation', 'Tag Location')}
|
|
159
|
+
</Button>
|
|
160
|
+
</>
|
|
161
|
+
</div>
|
|
162
|
+
</CardHeader>
|
|
163
|
+
|
|
164
|
+
<DataTable rows={paginatedRows} headers={tableHeaders} isSortable size={isTablet ? 'lg' : 'sm'} useZebraStyles>
|
|
165
|
+
{({ rows, headers, getTableProps }) => {
|
|
166
|
+
return (
|
|
167
|
+
<TableContainer>
|
|
168
|
+
<Table {...getTableProps()}>
|
|
169
|
+
<TableHead>
|
|
170
|
+
<TableRow>
|
|
171
|
+
{headers.map((header) => (
|
|
172
|
+
<TableHeader key={header.key}>{header.header}</TableHeader>
|
|
173
|
+
))}
|
|
174
|
+
</TableRow>
|
|
175
|
+
</TableHead>
|
|
176
|
+
<TableBody>
|
|
177
|
+
{rows.map((row) => (
|
|
178
|
+
<TableRow key={row.id}>
|
|
179
|
+
{row.cells.map((cell) => (
|
|
180
|
+
<TableCell key={cell.id}>
|
|
181
|
+
{cell.id.includes('tags') && cell.value !== 'No tags' ? (
|
|
182
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
|
|
183
|
+
{cell.value.split(', ').map((tag, index) => (
|
|
184
|
+
<Tag key={index} type="blue" size="sm">
|
|
185
|
+
{tag}
|
|
186
|
+
</Tag>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
) : (
|
|
190
|
+
cell.value
|
|
191
|
+
)}
|
|
192
|
+
</TableCell>
|
|
193
|
+
))}
|
|
194
|
+
</TableRow>
|
|
195
|
+
))}
|
|
196
|
+
</TableBody>
|
|
197
|
+
</Table>
|
|
198
|
+
{rows.length === 0 ? (
|
|
199
|
+
<div className={styles.tileContainer}>
|
|
200
|
+
<Tile className={styles.tile}>
|
|
201
|
+
<div className={styles.tileContent}>
|
|
202
|
+
<p className={styles.content}>
|
|
203
|
+
{searchTerm
|
|
204
|
+
? t('noSearchResults', `No results found for "${searchTerm}"`)
|
|
205
|
+
: t('noData', 'No data to display')}
|
|
206
|
+
</p>
|
|
207
|
+
<p className={styles.helper}>
|
|
208
|
+
{searchTerm
|
|
209
|
+
? t('tryDifferentSearch', 'Try a different search term')
|
|
210
|
+
: t('checkFilters', 'Check the filters above')}
|
|
211
|
+
</p>
|
|
212
|
+
</div>
|
|
213
|
+
</Tile>
|
|
214
|
+
</div>
|
|
215
|
+
) : null}
|
|
216
|
+
<Pagination
|
|
217
|
+
backwardText="Previous page"
|
|
218
|
+
forwardText="Next page"
|
|
219
|
+
page={currentPage}
|
|
220
|
+
pageSize={pageSize}
|
|
221
|
+
pageSizes={[10, 20, 30, 40, 50]}
|
|
222
|
+
totalItems={filteredRows.length}
|
|
223
|
+
onChange={({ pageSize: newPageSize, page }) => {
|
|
224
|
+
if (newPageSize !== pageSize) {
|
|
225
|
+
setPageSize(newPageSize);
|
|
226
|
+
setCurrentPage(1);
|
|
227
|
+
}
|
|
228
|
+
if (page !== currentPage) {
|
|
229
|
+
setCurrentPage(page);
|
|
230
|
+
}
|
|
231
|
+
}}
|
|
232
|
+
/>
|
|
233
|
+
</TableContainer>
|
|
234
|
+
);
|
|
235
|
+
}}
|
|
236
|
+
</DataTable>
|
|
237
|
+
</div>
|
|
238
|
+
<WorkspaceContainer contextKey="locations" />
|
|
239
|
+
</>
|
|
240
|
+
);
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
export default LocationsTable;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { FetchResponse, fhirBaseUrl, openmrsFetch } from '@openmrs/esm-framework';
|
|
2
|
+
import { type FHIRBundle, FHIRLocation, LocationTagsResponse } from '../types';
|
|
3
|
+
import useSWR from 'swr';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Fetches facilities that are tagged with any of the specified location tags.
|
|
7
|
+
*
|
|
8
|
+
* @param locationTags The location tags to filter facilities by
|
|
9
|
+
* @returns A promise that resolves to the data object containing the matching FHIR locations
|
|
10
|
+
* @throws {Error} If the API request fails
|
|
11
|
+
*/
|
|
12
|
+
export async function getFacilitiesByLocationTags(
|
|
13
|
+
locationTags: LocationTagsResponse,
|
|
14
|
+
): Promise<{ results: Array<FHIRLocation> }> {
|
|
15
|
+
const tagNames = locationTags.results
|
|
16
|
+
.map((tag) => encodeURIComponent(tag.name || tag.display))
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.join(',');
|
|
19
|
+
|
|
20
|
+
const url = `${fhirBaseUrl}/Location?_summary=data&_tag=${tagNames}`;
|
|
21
|
+
const response = await openmrsFetch<FHIRBundle>(url);
|
|
22
|
+
|
|
23
|
+
const locations = response.data?.entry?.map((entry) => entry.resource) ?? [];
|
|
24
|
+
|
|
25
|
+
return { results: locations };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const useFacilitiesTagged = (locationTags: LocationTagsResponse) => {
|
|
29
|
+
const tagNames = locationTags.results
|
|
30
|
+
.map((tag) => encodeURIComponent(tag.name || tag.display))
|
|
31
|
+
.filter(Boolean)
|
|
32
|
+
.join(',');
|
|
33
|
+
const url = `${fhirBaseUrl}/Location?_summary=data&_tag=${tagNames}`;
|
|
34
|
+
const { isLoading, error, data } = useSWR<FetchResponse<FHIRBundle>>(url, openmrsFetch);
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
isLoading,
|
|
38
|
+
error,
|
|
39
|
+
facilityList: data?.data?.entry || [],
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
|
+
|
|
5
|
+
.widgetCard {
|
|
6
|
+
background-color: colors.$white-0;
|
|
7
|
+
border: 1px solid colors.$gray-20;
|
|
8
|
+
margin-bottom: layout.$spacing-06;
|
|
9
|
+
margin-right: layout.$spacing-03;
|
|
10
|
+
margin-left: layout.$spacing-05;
|
|
11
|
+
}
|
|
12
|
+
.expandedRow {
|
|
13
|
+
background-color: colors.$white-0;
|
|
14
|
+
border: 1px solid colors.$gray-20;
|
|
15
|
+
margin-bottom: layout.$spacing-06;
|
|
16
|
+
margin-right: layout.$spacing-03;
|
|
17
|
+
margin-left: layout.$spacing-05;
|
|
18
|
+
}
|
|
19
|
+
.flexContainer {
|
|
20
|
+
display: flex;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
align-items: center;
|
|
23
|
+
padding: layout.$spacing-05;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.filterContainer {
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: layout.$spacing-05;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.headerActions {
|
|
33
|
+
display: flex;
|
|
34
|
+
gap: layout.$spacing-03;
|
|
35
|
+
align-items: center;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.filterContainer {
|
|
39
|
+
flex: 1;
|
|
40
|
+
|
|
41
|
+
:global(.cds--dropdown__wrapper--inline) {
|
|
42
|
+
gap: 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
:global(.cds--list-box__menu-icon) {
|
|
46
|
+
height: layout.$spacing-05;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.backgroundDataFetchingIndicator {
|
|
51
|
+
align-items: center;
|
|
52
|
+
display: flex;
|
|
53
|
+
justify-content: flex-end;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.tileContainer {
|
|
57
|
+
background-color: colors.$white-0;
|
|
58
|
+
border-top: 1px solid colors.$gray-20;
|
|
59
|
+
padding: layout.$spacing-09 0;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.tile {
|
|
63
|
+
margin: auto;
|
|
64
|
+
width: fit-content;
|
|
65
|
+
display: flex;
|
|
66
|
+
flex-direction: column;
|
|
67
|
+
align-items: center;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.tileContent {
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-direction: column;
|
|
73
|
+
align-items: center;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.content {
|
|
77
|
+
@include type.type-style('heading-compact-02');
|
|
78
|
+
color: colors.$gray-70;
|
|
79
|
+
margin-bottom: layout.$spacing-03;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.helper {
|
|
83
|
+
@include type.type-style('body-compact-01');
|
|
84
|
+
color: colors.$gray-70;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.separator {
|
|
88
|
+
@include type.type-style('body-compact-02');
|
|
89
|
+
color: colors.$gray-70;
|
|
90
|
+
width: 80%;
|
|
91
|
+
margin: layout.$spacing-06 auto;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
text-align: center;
|
|
94
|
+
|
|
95
|
+
&::before,
|
|
96
|
+
&::after {
|
|
97
|
+
background-color: colors.$gray-40;
|
|
98
|
+
content: '';
|
|
99
|
+
display: inline-block;
|
|
100
|
+
height: 1px;
|
|
101
|
+
position: relative;
|
|
102
|
+
vertical-align: middle;
|
|
103
|
+
width: 50%;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
&::before {
|
|
107
|
+
right: layout.$spacing-03;
|
|
108
|
+
margin-left: -50%;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
&::after {
|
|
112
|
+
left: layout.$spacing-03;
|
|
113
|
+
margin-right: -50%;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
export interface AdmissionLocationResponse {
|
|
2
|
+
uuid: string;
|
|
3
|
+
wardName: string;
|
|
4
|
+
totalBeds: number;
|
|
5
|
+
occupiedBeds: number;
|
|
6
|
+
availableBeds: number;
|
|
7
|
+
ward: {
|
|
8
|
+
uuid: string;
|
|
9
|
+
display: string;
|
|
10
|
+
name: string;
|
|
11
|
+
tags: Array<{
|
|
12
|
+
uuid: string;
|
|
13
|
+
display: string;
|
|
14
|
+
}>;
|
|
15
|
+
parentLocation: {
|
|
16
|
+
uuid: string;
|
|
17
|
+
display: string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface LocationTagsResponse {
|
|
23
|
+
results: Array<{
|
|
24
|
+
uuid: string;
|
|
25
|
+
display: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description: string;
|
|
28
|
+
}>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// OpenMRS format (legacy)
|
|
32
|
+
export interface LocationResponse {
|
|
33
|
+
uuid: string;
|
|
34
|
+
display: string;
|
|
35
|
+
name: string;
|
|
36
|
+
description: string;
|
|
37
|
+
stateProvince: string;
|
|
38
|
+
country: string;
|
|
39
|
+
countyDistrict: string;
|
|
40
|
+
address5: string;
|
|
41
|
+
address6: string;
|
|
42
|
+
tags: Array<{
|
|
43
|
+
uuid: string;
|
|
44
|
+
display: string;
|
|
45
|
+
name: string;
|
|
46
|
+
description: string;
|
|
47
|
+
}>;
|
|
48
|
+
attributes: Array<{
|
|
49
|
+
display: string;
|
|
50
|
+
uuid: string;
|
|
51
|
+
attributeType: {
|
|
52
|
+
uuid: string;
|
|
53
|
+
display: string;
|
|
54
|
+
};
|
|
55
|
+
value: string;
|
|
56
|
+
}>;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface FHIRLocation {
|
|
60
|
+
resourceType: 'Location';
|
|
61
|
+
id: string;
|
|
62
|
+
meta?: {
|
|
63
|
+
versionId?: string;
|
|
64
|
+
lastUpdated?: string;
|
|
65
|
+
tag?: Array<{
|
|
66
|
+
system: string;
|
|
67
|
+
code: string;
|
|
68
|
+
display: string;
|
|
69
|
+
}>;
|
|
70
|
+
};
|
|
71
|
+
status: string;
|
|
72
|
+
name: string;
|
|
73
|
+
description?: string;
|
|
74
|
+
address?: {
|
|
75
|
+
extension?: Array<{
|
|
76
|
+
url: string;
|
|
77
|
+
extension?: Array<{
|
|
78
|
+
url: string;
|
|
79
|
+
valueString: string;
|
|
80
|
+
}>;
|
|
81
|
+
}>;
|
|
82
|
+
line?: string[];
|
|
83
|
+
city?: string;
|
|
84
|
+
district?: string;
|
|
85
|
+
state?: string;
|
|
86
|
+
country?: string;
|
|
87
|
+
};
|
|
88
|
+
identifier?: Array<{
|
|
89
|
+
system: string;
|
|
90
|
+
value: string;
|
|
91
|
+
}>;
|
|
92
|
+
partOf?: {
|
|
93
|
+
reference: string;
|
|
94
|
+
type: string;
|
|
95
|
+
display: string;
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface FHIRBundle {
|
|
100
|
+
resourceType: 'Bundle';
|
|
101
|
+
id: string;
|
|
102
|
+
meta?: {
|
|
103
|
+
lastUpdated?: string;
|
|
104
|
+
tag?: Array<{
|
|
105
|
+
system: string;
|
|
106
|
+
code: string;
|
|
107
|
+
display: string;
|
|
108
|
+
}>;
|
|
109
|
+
};
|
|
110
|
+
type: string;
|
|
111
|
+
total?: number;
|
|
112
|
+
link?: Array<{
|
|
113
|
+
relation: string;
|
|
114
|
+
url: string;
|
|
115
|
+
}>;
|
|
116
|
+
entry?: Array<{
|
|
117
|
+
fullUrl: string;
|
|
118
|
+
resource: FHIRLocation;
|
|
119
|
+
}>;
|
|
120
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,8 @@ import HWRConfirmModal from './components/modal/hwr-confirmation.modal';
|
|
|
9
9
|
import HWREmptyModal from './components/modal/hwr-empty.modal.component';
|
|
10
10
|
import UserRoleScopeWorkspace from './components/users/manage-users/manage-user-role-scope/user-role-scope-workspace/user-role-scope.workspace';
|
|
11
11
|
import HWRSyncModal from './components/modal/hwr-sync.modal';
|
|
12
|
+
import AddLocationWorkspace from './components/locations/forms/add-location/add-location.workspace';
|
|
13
|
+
import SearchLocationWorkspace from './components/locations/forms/search-location/search-location.workspace';
|
|
12
14
|
|
|
13
15
|
const options = {
|
|
14
16
|
featureName: 'esm-admin-app',
|
|
@@ -36,6 +38,10 @@ export const etlAdministrationLeftPannelLink = getSyncLifecycle(
|
|
|
36
38
|
createLeftPanelLink({ title: 'ETL Administration', name: 'etl-administration' }),
|
|
37
39
|
options,
|
|
38
40
|
);
|
|
41
|
+
export const locationsLeftPanelLink = getSyncLifecycle(
|
|
42
|
+
createLeftPanelLink({ title: 'Locations', name: 'locations' }),
|
|
43
|
+
options,
|
|
44
|
+
);
|
|
39
45
|
export const facilitySetupLeftPanelLink = getSyncLifecycle(
|
|
40
46
|
createLeftPanelLink({ title: 'Facility Details', name: 'facility-setup' }),
|
|
41
47
|
options,
|
|
@@ -44,3 +50,6 @@ export const facilitySetupLeftPanelLink = getSyncLifecycle(
|
|
|
44
50
|
export const hwrConfirmationModal = getSyncLifecycle(HWRConfirmModal, options);
|
|
45
51
|
export const hwrEmptyModal = getSyncLifecycle(HWREmptyModal, options);
|
|
46
52
|
export const hwrSyncModal = getSyncLifecycle(HWRSyncModal, options);
|
|
53
|
+
|
|
54
|
+
export const addLocation = getSyncLifecycle(AddLocationWorkspace, options);
|
|
55
|
+
export const searchLocationWorkspace = getSyncLifecycle(SearchLocationWorkspace, options);
|
package/src/root.component.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import LeftPanel from './components/side-menu/left-pannel.component';
|
|
|
6
6
|
import UserManagentLandingPage from './components/users/manage-users/manage-user.component';
|
|
7
7
|
import EtlAdminDashboard from './components/dashboard/etl-dashboard.component';
|
|
8
8
|
import FacilitySetup from './components/facility-setup/facility-setup.component';
|
|
9
|
+
import HomeComponent from './components/locations/home/home-locations.component';
|
|
9
10
|
|
|
10
11
|
const Root: React.FC = () => {
|
|
11
12
|
const spaBasePath = window.spaBase;
|
|
@@ -28,6 +29,7 @@ const Root: React.FC = () => {
|
|
|
28
29
|
<Route path="/user-management" element={<UserManagentLandingPage />} />
|
|
29
30
|
<Route path="/etl-administration" element={<EtlAdminDashboard />} />
|
|
30
31
|
<Route path="/facility-setup" element={<FacilitySetup />} />
|
|
32
|
+
<Route path="/locations" element={<HomeComponent />} />
|
|
31
33
|
</Routes>
|
|
32
34
|
</main>
|
|
33
35
|
</BrowserRouter>
|
package/src/routes.json
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"component": "userManagementLeftPannelLink",
|
|
14
14
|
"name": "user-management-left-panel-link",
|
|
15
15
|
"slot": "admin-left-panel-slot"
|
|
16
|
-
},
|
|
16
|
+
},
|
|
17
17
|
{
|
|
18
|
-
"component": "
|
|
19
|
-
"name": "
|
|
18
|
+
"component": "locationsLeftPanelLink",
|
|
19
|
+
"name": "locations-left-panel-link",
|
|
20
20
|
"slot": "admin-left-panel-slot"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
@@ -41,6 +41,30 @@
|
|
|
41
41
|
"type": "other-form",
|
|
42
42
|
"canMaximize": true,
|
|
43
43
|
"width": "extra-wide"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"name": "add-location-workspace",
|
|
47
|
+
"title": "Add Location",
|
|
48
|
+
"component": "addLocation",
|
|
49
|
+
"type": "workspace"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"name": "search-location-workspace",
|
|
53
|
+
"title": "Search Location",
|
|
54
|
+
"component": "searchLocationWorkspace",
|
|
55
|
+
"type": "workspace"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"name": "hwr-sync-workspace",
|
|
59
|
+
"title": "HWR Sync Workspace",
|
|
60
|
+
"component": "hwrSyncWorkspace",
|
|
61
|
+
"type": "other-form"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "hwr-sync-modal",
|
|
65
|
+
"title": "HWR Sync Modal",
|
|
66
|
+
"component": "hwrSyncModal",
|
|
67
|
+
"type": "modal"
|
|
44
68
|
}
|
|
45
69
|
],
|
|
46
70
|
"modals": [
|