@pih/esm-audit-app 2.1.1-pre.144 → 2.1.1-pre.151
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 +1 -1
- package/README.md +96 -2
- package/dist/115.js +1 -1
- package/dist/117.js +1 -1
- package/dist/117.js.map +1 -1
- package/dist/508.js +1 -1
- package/dist/508.js.map +1 -1
- package/dist/514.js +1 -0
- package/dist/514.js.map +1 -0
- package/dist/912.js +1 -1
- package/dist/912.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/pih-esm-audit-app.js +1 -1
- package/dist/pih-esm-audit-app.js.buildmanifest.json +34 -34
- package/dist/routes.json +1 -1
- package/locales/en/translation.json +118 -0
- package/locales/fr/translation.json +118 -0
- package/package.json +1 -1
- package/src/audit/audit-search.component.tsx +58 -0
- package/src/audit/audit.component.tsx +63 -16
- package/src/audit/audit.resource.ts +445 -10
- package/src/audit/audit.scss +6 -0
- package/src/audit/date-range-picker.test-stub.tsx +40 -0
- package/src/audit/date-range.test.ts +128 -0
- package/src/audit/date-range.ts +120 -0
- package/src/audit/encounter-filters.component.tsx +40 -18
- package/src/audit/encounter-filters.test.ts +1 -2
- package/src/audit/encounter-filters.ts +2 -27
- package/src/audit/patient-record.component.test.tsx +3 -5
- package/src/audit/patient-record.component.tsx +13 -2
- package/src/audit/patient-search.component.tsx +1 -1
- package/src/audit/provider-encounters.component.test.tsx +254 -0
- package/src/audit/provider-encounters.component.tsx +160 -0
- package/src/audit/provider-search.component.test.tsx +72 -0
- package/src/audit/provider-search.component.tsx +116 -0
- package/src/audit/user-encounters.component.test.tsx +431 -0
- package/src/audit/user-encounters.component.tsx +239 -0
- package/src/audit/user-encounters.test.ts +176 -0
- package/src/audit/user-encounters.ts +166 -0
- package/src/audit/user-search.component.test.tsx +89 -0
- package/src/audit/user-search.component.tsx +111 -0
- package/src/root.component.test.tsx +48 -3
- package/src/types.ts +34 -0
- package/translations/en.json +29 -0
- package/translations/es.json +29 -0
- package/translations/fr.json +29 -0
- package/dist/446.js +0 -1
- package/dist/446.js.map +0 -1
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"auxiliaryFiles": [
|
|
22
22
|
"115.js.map"
|
|
23
23
|
],
|
|
24
|
-
"hash": "
|
|
24
|
+
"hash": "9b41c7e832a25bd9",
|
|
25
25
|
"childrenByOrder": {}
|
|
26
26
|
},
|
|
27
27
|
{
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"rendered": true,
|
|
30
30
|
"initial": false,
|
|
31
31
|
"entry": false,
|
|
32
|
-
"size":
|
|
32
|
+
"size": 5110,
|
|
33
33
|
"sizes": {
|
|
34
|
-
"javascript":
|
|
34
|
+
"javascript": 5110
|
|
35
35
|
},
|
|
36
36
|
"names": [],
|
|
37
37
|
"idHints": [],
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"auxiliaryFiles": [
|
|
46
46
|
"117.js.map"
|
|
47
47
|
],
|
|
48
|
-
"hash": "
|
|
48
|
+
"hash": "0882b3e8a79e8e83",
|
|
49
49
|
"childrenByOrder": {}
|
|
50
50
|
},
|
|
51
51
|
{
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
"auxiliaryFiles": [
|
|
74
74
|
"pih-esm-audit-app.js.map"
|
|
75
75
|
],
|
|
76
|
-
"hash": "
|
|
76
|
+
"hash": "023ea685c1be22f5",
|
|
77
77
|
"childrenByOrder": {}
|
|
78
78
|
},
|
|
79
79
|
{
|
|
@@ -155,23 +155,26 @@
|
|
|
155
155
|
"rendered": true,
|
|
156
156
|
"initial": false,
|
|
157
157
|
"entry": false,
|
|
158
|
-
"
|
|
158
|
+
"reason": "split chunk (cache group: defaultVendors)",
|
|
159
|
+
"size": 1408999,
|
|
159
160
|
"sizes": {
|
|
160
|
-
"javascript":
|
|
161
|
+
"javascript": 1408999
|
|
161
162
|
},
|
|
162
163
|
"names": [],
|
|
163
|
-
"idHints": [
|
|
164
|
+
"idHints": [
|
|
165
|
+
"vendors"
|
|
166
|
+
],
|
|
164
167
|
"runtime": [
|
|
165
168
|
"@pih/esm-audit-app",
|
|
166
169
|
"main"
|
|
167
170
|
],
|
|
168
171
|
"files": [
|
|
169
|
-
"
|
|
172
|
+
"455.js"
|
|
170
173
|
],
|
|
171
174
|
"auxiliaryFiles": [
|
|
172
|
-
"
|
|
175
|
+
"455.js.map"
|
|
173
176
|
],
|
|
174
|
-
"hash": "
|
|
177
|
+
"hash": "5342a8efbabac44a",
|
|
175
178
|
"childrenByOrder": {}
|
|
176
179
|
},
|
|
177
180
|
{
|
|
@@ -179,26 +182,23 @@
|
|
|
179
182
|
"rendered": true,
|
|
180
183
|
"initial": false,
|
|
181
184
|
"entry": false,
|
|
182
|
-
"
|
|
183
|
-
"size": 1408999,
|
|
185
|
+
"size": 7103,
|
|
184
186
|
"sizes": {
|
|
185
|
-
"javascript":
|
|
187
|
+
"javascript": 7103
|
|
186
188
|
},
|
|
187
189
|
"names": [],
|
|
188
|
-
"idHints": [
|
|
189
|
-
"vendors"
|
|
190
|
-
],
|
|
190
|
+
"idHints": [],
|
|
191
191
|
"runtime": [
|
|
192
192
|
"@pih/esm-audit-app",
|
|
193
193
|
"main"
|
|
194
194
|
],
|
|
195
195
|
"files": [
|
|
196
|
-
"
|
|
196
|
+
"466.js"
|
|
197
197
|
],
|
|
198
198
|
"auxiliaryFiles": [
|
|
199
|
-
"
|
|
199
|
+
"466.js.map"
|
|
200
200
|
],
|
|
201
|
-
"hash": "
|
|
201
|
+
"hash": "c30f3521edea15c4",
|
|
202
202
|
"childrenByOrder": {}
|
|
203
203
|
},
|
|
204
204
|
{
|
|
@@ -206,9 +206,9 @@
|
|
|
206
206
|
"rendered": true,
|
|
207
207
|
"initial": false,
|
|
208
208
|
"entry": false,
|
|
209
|
-
"size":
|
|
209
|
+
"size": 6134,
|
|
210
210
|
"sizes": {
|
|
211
|
-
"javascript":
|
|
211
|
+
"javascript": 6134
|
|
212
212
|
},
|
|
213
213
|
"names": [],
|
|
214
214
|
"idHints": [],
|
|
@@ -217,12 +217,12 @@
|
|
|
217
217
|
"main"
|
|
218
218
|
],
|
|
219
219
|
"files": [
|
|
220
|
-
"
|
|
220
|
+
"508.js"
|
|
221
221
|
],
|
|
222
222
|
"auxiliaryFiles": [
|
|
223
|
-
"
|
|
223
|
+
"508.js.map"
|
|
224
224
|
],
|
|
225
|
-
"hash": "
|
|
225
|
+
"hash": "3b9069416bf40695",
|
|
226
226
|
"childrenByOrder": {}
|
|
227
227
|
},
|
|
228
228
|
{
|
|
@@ -230,9 +230,9 @@
|
|
|
230
230
|
"rendered": true,
|
|
231
231
|
"initial": false,
|
|
232
232
|
"entry": false,
|
|
233
|
-
"size":
|
|
233
|
+
"size": 250106,
|
|
234
234
|
"sizes": {
|
|
235
|
-
"javascript":
|
|
235
|
+
"javascript": 250106
|
|
236
236
|
},
|
|
237
237
|
"names": [],
|
|
238
238
|
"idHints": [],
|
|
@@ -241,12 +241,12 @@
|
|
|
241
241
|
"main"
|
|
242
242
|
],
|
|
243
243
|
"files": [
|
|
244
|
-
"
|
|
244
|
+
"514.js"
|
|
245
245
|
],
|
|
246
246
|
"auxiliaryFiles": [
|
|
247
|
-
"
|
|
247
|
+
"514.js.map"
|
|
248
248
|
],
|
|
249
|
-
"hash": "
|
|
249
|
+
"hash": "d648c72c6a3e4231",
|
|
250
250
|
"childrenByOrder": {}
|
|
251
251
|
},
|
|
252
252
|
{
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
"auxiliaryFiles": [
|
|
475
475
|
"main.js.map"
|
|
476
476
|
],
|
|
477
|
-
"hash": "
|
|
477
|
+
"hash": "b4eb44e7a939436c",
|
|
478
478
|
"childrenByOrder": {}
|
|
479
479
|
},
|
|
480
480
|
{
|
|
@@ -482,9 +482,9 @@
|
|
|
482
482
|
"rendered": true,
|
|
483
483
|
"initial": false,
|
|
484
484
|
"entry": false,
|
|
485
|
-
"size":
|
|
485
|
+
"size": 5812,
|
|
486
486
|
"sizes": {
|
|
487
|
-
"javascript":
|
|
487
|
+
"javascript": 5812
|
|
488
488
|
},
|
|
489
489
|
"names": [],
|
|
490
490
|
"idHints": [],
|
|
@@ -498,7 +498,7 @@
|
|
|
498
498
|
"auxiliaryFiles": [
|
|
499
499
|
"912.js.map"
|
|
500
500
|
],
|
|
501
|
-
"hash": "
|
|
501
|
+
"hash": "40f746c2367391da",
|
|
502
502
|
"childrenByOrder": {}
|
|
503
503
|
},
|
|
504
504
|
{
|
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","extensions":[{"name":"audit-dashboard-link","slot":"homepage-dashboard-slot","component":"auditDashboardLink","meta":{"name":"audit","slot":"audit-dashboard-slot","title":"Audit"}},{"name":"audit-dashboard","slot":"audit-dashboard-slot","component":"root"}],"version":"2.1.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","extensions":[{"name":"audit-dashboard-link","slot":"homepage-dashboard-slot","component":"auditDashboardLink","meta":{"name":"audit","slot":"audit-dashboard-slot","title":"Audit"}},{"name":"audit-dashboard","slot":"audit-dashboard-slot","component":"root"}],"version":"2.1.1-pre.151"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"searchBy": "Search by",
|
|
3
|
+
"patients": "Patients",
|
|
4
|
+
"users": "Users",
|
|
5
|
+
"providers": "Providers",
|
|
6
|
+
"auditTrail": "Audit trail",
|
|
7
|
+
"encounterAudit": "Encounter audit",
|
|
8
|
+
"encounterNotFound": "That encounter could not be found.",
|
|
9
|
+
"patient": "Patient",
|
|
10
|
+
"identifier": "Identifier",
|
|
11
|
+
"encounterDate": "Encounter date",
|
|
12
|
+
"location": "Location",
|
|
13
|
+
"visit": "Visit",
|
|
14
|
+
"encounterType": "Encounter type",
|
|
15
|
+
"form": "Form",
|
|
16
|
+
"createdBy": "Created by",
|
|
17
|
+
"changedBy": "Changed by",
|
|
18
|
+
"deletedBy": "Deleted by",
|
|
19
|
+
"deletionReason": "Reason for deletion",
|
|
20
|
+
"encounter": "Encounter",
|
|
21
|
+
"deleted": "Deleted",
|
|
22
|
+
"encounterDeletedSubtitle": "This encounter has been deleted. Its observations are shown as they were left.",
|
|
23
|
+
"encounterDeleted": "Deleted encounter",
|
|
24
|
+
"encounterSummary": "Encounter summary",
|
|
25
|
+
"noProviders": "No providers are recorded for this encounter.",
|
|
26
|
+
"role": "Role",
|
|
27
|
+
"providerName": "Provider name",
|
|
28
|
+
"backToEncounters": "Back to encounters",
|
|
29
|
+
"allEncounterTypes": "All encounter types",
|
|
30
|
+
"encounterDateRange": "Encounter date range",
|
|
31
|
+
"clearFilters": "Clear filters",
|
|
32
|
+
"edited": "Edited",
|
|
33
|
+
"editedLegend": "the value was changed after it was first recorded",
|
|
34
|
+
"addedLater": "Added later",
|
|
35
|
+
"addedLaterLegend": "recorded after the encounter itself was created",
|
|
36
|
+
"deletedLegend": "removed from the encounter",
|
|
37
|
+
"previousValue": "Previous value",
|
|
38
|
+
"previousValueLegend": "the value that an edit replaced",
|
|
39
|
+
"observations": "Observations",
|
|
40
|
+
"showDeletedObs": "Show deleted observations",
|
|
41
|
+
"showConceptDescriptions": "Show concept descriptions",
|
|
42
|
+
"noObservations": "This encounter has no observations.",
|
|
43
|
+
"questionConcept": "Question concept",
|
|
44
|
+
"value": "Value",
|
|
45
|
+
"change": "Change",
|
|
46
|
+
"recordedBy": "Recorded by",
|
|
47
|
+
"wasPreviously": "was {{value}}",
|
|
48
|
+
"changedByUser": "Changed by {{user}}",
|
|
49
|
+
"encounterCreatedAction": "Created encounter",
|
|
50
|
+
"encounterChangedAction": "Changed encounter",
|
|
51
|
+
"encounterDeletedAction": "Deleted encounter",
|
|
52
|
+
"obsRecordedAction": "Recorded observation",
|
|
53
|
+
"obsEditedAction": "Edited observation",
|
|
54
|
+
"obsDeletedAction": "Deleted observation",
|
|
55
|
+
"recordActivity": "Patient activity",
|
|
56
|
+
"readingObservations": "Reading observations from {{read}} of {{total}} encounters…",
|
|
57
|
+
"noActivity": "There is no recorded activity to show.",
|
|
58
|
+
"whoTouchedThisRecord": "Who modified this patient record",
|
|
59
|
+
"user": "User",
|
|
60
|
+
"encountersCreated": "Encounters created",
|
|
61
|
+
"obsRecordedCount": "Observations recorded",
|
|
62
|
+
"obsEditedCount": "Observations edited",
|
|
63
|
+
"obsDeletedCount": "Observations deleted",
|
|
64
|
+
"firstActivity": "First activity",
|
|
65
|
+
"lastActivity": "Last activity",
|
|
66
|
+
"showOnlyThisUser": "Show only this user's activity",
|
|
67
|
+
"unknownUser": "Unknown user",
|
|
68
|
+
"activityLog": "Activity log",
|
|
69
|
+
"showEveryone": "Show everyone",
|
|
70
|
+
"when": "When",
|
|
71
|
+
"action": "Action",
|
|
72
|
+
"detail": "Detail",
|
|
73
|
+
"encounters": "Encounters",
|
|
74
|
+
"noEncountersMatchFilters": "No encounters match these filters.",
|
|
75
|
+
"noEncountersFound": "This patient has no encounters to audit.",
|
|
76
|
+
"provider": "Provider",
|
|
77
|
+
"status": "Status",
|
|
78
|
+
"backToPatientSearch": "Back to patient search",
|
|
79
|
+
"includeDeletedEncounters": "Include deleted encounters",
|
|
80
|
+
"cannotIncludeDeletedEncounters": "Deleted encounters can only be listed for a patient who has an identifier.",
|
|
81
|
+
"deletedEncountersUnavailable": "Deleted encounters unavailable",
|
|
82
|
+
"auditViews": "Audit views",
|
|
83
|
+
"searchForPatient": "Search for a patient by name or identifier",
|
|
84
|
+
"searchToBegin": "Enter a patient name or identifier to begin auditing their records.",
|
|
85
|
+
"patientSearchResults": "Patient search results",
|
|
86
|
+
"noPatientsFound": "No patients match \"{{searchTerm}}\".",
|
|
87
|
+
"patientName": "Patient name",
|
|
88
|
+
"gender": "Gender",
|
|
89
|
+
"age": "Age",
|
|
90
|
+
"birthdate": "Date of birth",
|
|
91
|
+
"backToProviderSearch": "Back to provider search",
|
|
92
|
+
"providerEncountersScope": "These are the encounters this provider is recorded on, which is not the same as the ones they entered or changed — the last column names who did that, and the Users tab searches by it.",
|
|
93
|
+
"recordedAsProvider": "Recorded as provider",
|
|
94
|
+
"noProviderEncountersMatch": "No encounters for this provider match these filters.",
|
|
95
|
+
"noProviderEncounters": "This provider is not recorded on any encounters.",
|
|
96
|
+
"searchForProvider": "Search for a provider by name or identifier",
|
|
97
|
+
"searchProviderToBegin": "Enter a provider name or identifier to see the encounters they are recorded on.",
|
|
98
|
+
"providerSearchResults": "Provider search results",
|
|
99
|
+
"noProvidersFound": "No providers match \"{{searchTerm}}\".",
|
|
100
|
+
"backToUserSearch": "Back to user search",
|
|
101
|
+
"changedBetween": "Changed between",
|
|
102
|
+
"readingUserObservations": "Read {{obsRead}} observations so far…",
|
|
103
|
+
"noUserEncountersMatch": "This user did not change any observations matching these filters.",
|
|
104
|
+
"noUserEncounters": "This user has not recorded or deleted any observations.",
|
|
105
|
+
"userScanStoppedEarly": "Stopped after reading {{obsRead}} observation changes without filling the page. Narrow the date range to search further back.",
|
|
106
|
+
"partialUserScan": "Part of the trail",
|
|
107
|
+
"lastChanged": "Last changed",
|
|
108
|
+
"lookingForMoreEncounters": "Looking for more encounters…",
|
|
109
|
+
"encounterRange": "Encounters {{from}}–{{to}}",
|
|
110
|
+
"searchForUser": "Search for a user by name or username",
|
|
111
|
+
"searchUserToBegin": "Enter a name or username to see the encounters that user has modified.",
|
|
112
|
+
"userSearchResults": "User search results",
|
|
113
|
+
"noUsersFound": "No users match \"{{searchTerm}}\".",
|
|
114
|
+
"name": "Name",
|
|
115
|
+
"username": "Username",
|
|
116
|
+
"retired": "Retired",
|
|
117
|
+
"audit": ""
|
|
118
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
{
|
|
2
|
+
"searchBy": "Search by",
|
|
3
|
+
"patients": "Patients",
|
|
4
|
+
"users": "Users",
|
|
5
|
+
"providers": "Providers",
|
|
6
|
+
"auditTrail": "Audit trail",
|
|
7
|
+
"encounterAudit": "Encounter audit",
|
|
8
|
+
"encounterNotFound": "That encounter could not be found.",
|
|
9
|
+
"patient": "Patient",
|
|
10
|
+
"identifier": "Identifier",
|
|
11
|
+
"encounterDate": "Encounter date",
|
|
12
|
+
"location": "Location",
|
|
13
|
+
"visit": "Visit",
|
|
14
|
+
"encounterType": "Encounter type",
|
|
15
|
+
"form": "Form",
|
|
16
|
+
"createdBy": "Created by",
|
|
17
|
+
"changedBy": "Changed by",
|
|
18
|
+
"deletedBy": "Deleted by",
|
|
19
|
+
"deletionReason": "Reason for deletion",
|
|
20
|
+
"encounter": "Encounter",
|
|
21
|
+
"deleted": "Deleted",
|
|
22
|
+
"encounterDeletedSubtitle": "This encounter has been deleted. Its observations are shown as they were left.",
|
|
23
|
+
"encounterDeleted": "Deleted encounter",
|
|
24
|
+
"encounterSummary": "Encounter summary",
|
|
25
|
+
"noProviders": "No providers are recorded for this encounter.",
|
|
26
|
+
"role": "Role",
|
|
27
|
+
"providerName": "Provider name",
|
|
28
|
+
"backToEncounters": "Back to encounters",
|
|
29
|
+
"allEncounterTypes": "All encounter types",
|
|
30
|
+
"encounterDateRange": "Encounter date range",
|
|
31
|
+
"clearFilters": "Clear filters",
|
|
32
|
+
"edited": "Edited",
|
|
33
|
+
"editedLegend": "the value was changed after it was first recorded",
|
|
34
|
+
"addedLater": "Added later",
|
|
35
|
+
"addedLaterLegend": "recorded after the encounter itself was created",
|
|
36
|
+
"deletedLegend": "removed from the encounter",
|
|
37
|
+
"previousValue": "Previous value",
|
|
38
|
+
"previousValueLegend": "the value that an edit replaced",
|
|
39
|
+
"observations": "Observations",
|
|
40
|
+
"showDeletedObs": "Show deleted observations",
|
|
41
|
+
"showConceptDescriptions": "Show concept descriptions",
|
|
42
|
+
"noObservations": "This encounter has no observations.",
|
|
43
|
+
"questionConcept": "Question concept",
|
|
44
|
+
"value": "Value",
|
|
45
|
+
"change": "Change",
|
|
46
|
+
"recordedBy": "Recorded by",
|
|
47
|
+
"wasPreviously": "was {{value}}",
|
|
48
|
+
"changedByUser": "Changed by {{user}}",
|
|
49
|
+
"encounterCreatedAction": "Created encounter",
|
|
50
|
+
"encounterChangedAction": "Changed encounter",
|
|
51
|
+
"encounterDeletedAction": "Deleted encounter",
|
|
52
|
+
"obsRecordedAction": "Recorded observation",
|
|
53
|
+
"obsEditedAction": "Edited observation",
|
|
54
|
+
"obsDeletedAction": "Deleted observation",
|
|
55
|
+
"recordActivity": "Patient activity",
|
|
56
|
+
"readingObservations": "Reading observations from {{read}} of {{total}} encounters…",
|
|
57
|
+
"noActivity": "There is no recorded activity to show.",
|
|
58
|
+
"whoTouchedThisRecord": "Who modified this patient record",
|
|
59
|
+
"user": "User",
|
|
60
|
+
"encountersCreated": "Encounters created",
|
|
61
|
+
"obsRecordedCount": "Observations recorded",
|
|
62
|
+
"obsEditedCount": "Observations edited",
|
|
63
|
+
"obsDeletedCount": "Observations deleted",
|
|
64
|
+
"firstActivity": "First activity",
|
|
65
|
+
"lastActivity": "Last activity",
|
|
66
|
+
"showOnlyThisUser": "Show only this user's activity",
|
|
67
|
+
"unknownUser": "Unknown user",
|
|
68
|
+
"activityLog": "Activity log",
|
|
69
|
+
"showEveryone": "Show everyone",
|
|
70
|
+
"when": "When",
|
|
71
|
+
"action": "Action",
|
|
72
|
+
"detail": "Detail",
|
|
73
|
+
"encounters": "Encounters",
|
|
74
|
+
"noEncountersMatchFilters": "No encounters match these filters.",
|
|
75
|
+
"noEncountersFound": "This patient has no encounters to audit.",
|
|
76
|
+
"provider": "Provider",
|
|
77
|
+
"status": "Status",
|
|
78
|
+
"backToPatientSearch": "Back to patient search",
|
|
79
|
+
"includeDeletedEncounters": "Include deleted encounters",
|
|
80
|
+
"cannotIncludeDeletedEncounters": "Deleted encounters can only be listed for a patient who has an identifier.",
|
|
81
|
+
"deletedEncountersUnavailable": "Deleted encounters unavailable",
|
|
82
|
+
"auditViews": "Audit views",
|
|
83
|
+
"searchForPatient": "Search for a patient by name or identifier",
|
|
84
|
+
"searchToBegin": "Enter a patient name or identifier to begin auditing their records.",
|
|
85
|
+
"patientSearchResults": "Patient search results",
|
|
86
|
+
"noPatientsFound": "No patients match \"{{searchTerm}}\".",
|
|
87
|
+
"patientName": "Patient name",
|
|
88
|
+
"gender": "Gender",
|
|
89
|
+
"age": "Age",
|
|
90
|
+
"birthdate": "Date of birth",
|
|
91
|
+
"backToProviderSearch": "Back to provider search",
|
|
92
|
+
"providerEncountersScope": "These are the encounters this provider is recorded on, which is not the same as the ones they entered or changed — the last column names who did that, and the Users tab searches by it.",
|
|
93
|
+
"recordedAsProvider": "Recorded as provider",
|
|
94
|
+
"noProviderEncountersMatch": "No encounters for this provider match these filters.",
|
|
95
|
+
"noProviderEncounters": "This provider is not recorded on any encounters.",
|
|
96
|
+
"searchForProvider": "Search for a provider by name or identifier",
|
|
97
|
+
"searchProviderToBegin": "Enter a provider name or identifier to see the encounters they are recorded on.",
|
|
98
|
+
"providerSearchResults": "Provider search results",
|
|
99
|
+
"noProvidersFound": "No providers match \"{{searchTerm}}\".",
|
|
100
|
+
"backToUserSearch": "Back to user search",
|
|
101
|
+
"changedBetween": "Changed between",
|
|
102
|
+
"readingUserObservations": "Read {{obsRead}} observations so far…",
|
|
103
|
+
"noUserEncountersMatch": "This user did not change any observations matching these filters.",
|
|
104
|
+
"noUserEncounters": "This user has not recorded or deleted any observations.",
|
|
105
|
+
"userScanStoppedEarly": "Stopped after reading {{obsRead}} observation changes without filling the page. Narrow the date range to search further back.",
|
|
106
|
+
"partialUserScan": "Part of the trail",
|
|
107
|
+
"lastChanged": "Last changed",
|
|
108
|
+
"lookingForMoreEncounters": "Looking for more encounters…",
|
|
109
|
+
"encounterRange": "Encounters {{from}}–{{to}}",
|
|
110
|
+
"searchForUser": "Search for a user by name or username",
|
|
111
|
+
"searchUserToBegin": "Enter a name or username to see the encounters that user has modified.",
|
|
112
|
+
"userSearchResults": "User search results",
|
|
113
|
+
"noUsersFound": "No users match \"{{searchTerm}}\".",
|
|
114
|
+
"name": "Name",
|
|
115
|
+
"username": "Username",
|
|
116
|
+
"retired": "Retired",
|
|
117
|
+
"audit": ""
|
|
118
|
+
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Tab, TabList, TabPanel, TabPanels, Tabs } from '@carbon/react';
|
|
4
|
+
import PatientSearch from './patient-search.component';
|
|
5
|
+
import ProviderSearch from './provider-search.component';
|
|
6
|
+
import UserSearch from './user-search.component';
|
|
7
|
+
import styles from './audit.scss';
|
|
8
|
+
|
|
9
|
+
export type AuditSearchTab = 'patients' | 'users' | 'providers';
|
|
10
|
+
|
|
11
|
+
export const auditSearchTabs: Array<AuditSearchTab> = ['patients', 'users', 'providers'];
|
|
12
|
+
|
|
13
|
+
interface AuditSearchProps {
|
|
14
|
+
tab: AuditSearchTab;
|
|
15
|
+
onSelectTab(tab: AuditSearchTab): void;
|
|
16
|
+
onSelectPatient(patientUuid: string): void;
|
|
17
|
+
onSelectUser(userUuid: string): void;
|
|
18
|
+
onSelectProvider(providerUuid: string): void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Where the audit trail starts: find the record to audit — by the patient it belongs to, by the
|
|
23
|
+
* user who changed it, or by a provider recorded on it.
|
|
24
|
+
*/
|
|
25
|
+
export default function AuditSearch({
|
|
26
|
+
tab,
|
|
27
|
+
onSelectTab,
|
|
28
|
+
onSelectPatient,
|
|
29
|
+
onSelectUser,
|
|
30
|
+
onSelectProvider,
|
|
31
|
+
}: AuditSearchProps) {
|
|
32
|
+
const { t } = useTranslation();
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<div className={styles.section}>
|
|
36
|
+
<Tabs
|
|
37
|
+
onChange={({ selectedIndex }) => onSelectTab(auditSearchTabs[selectedIndex])}
|
|
38
|
+
selectedIndex={Math.max(0, auditSearchTabs.indexOf(tab))}>
|
|
39
|
+
<TabList aria-label={t('searchBy', 'Search by')}>
|
|
40
|
+
<Tab>{t('patients', 'Patients')}</Tab>
|
|
41
|
+
<Tab>{t('users', 'Users')}</Tab>
|
|
42
|
+
<Tab>{t('providers', 'Providers')}</Tab>
|
|
43
|
+
</TabList>
|
|
44
|
+
<TabPanels>
|
|
45
|
+
<TabPanel>
|
|
46
|
+
<PatientSearch onSelectPatient={onSelectPatient} />
|
|
47
|
+
</TabPanel>
|
|
48
|
+
<TabPanel>
|
|
49
|
+
<UserSearch onSelectUser={onSelectUser} />
|
|
50
|
+
</TabPanel>
|
|
51
|
+
<TabPanel>
|
|
52
|
+
<ProviderSearch onSelectProvider={onSelectProvider} />
|
|
53
|
+
</TabPanel>
|
|
54
|
+
</TabPanels>
|
|
55
|
+
</Tabs>
|
|
56
|
+
</div>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
@@ -2,42 +2,65 @@ import React, { useCallback } from 'react';
|
|
|
2
2
|
import { useSearchParams } from 'react-router-dom';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { PageHeader, PageHeaderContent, PatientSearchPictogram } from '@openmrs/esm-framework';
|
|
5
|
+
import AuditSearch, { type AuditSearchTab, auditSearchTabs } from './audit-search.component';
|
|
5
6
|
import EncounterAudit from './encounter-audit.component';
|
|
6
7
|
import PatientRecord, { type PatientRecordView, patientRecordViews } from './patient-record.component';
|
|
7
|
-
import
|
|
8
|
+
import ProviderEncounters from './provider-encounters.component';
|
|
9
|
+
import UserEncounters from './user-encounters.component';
|
|
8
10
|
import styles from './audit.scss';
|
|
9
11
|
|
|
12
|
+
interface AuditLocation {
|
|
13
|
+
patient?: string;
|
|
14
|
+
user?: string;
|
|
15
|
+
provider?: string;
|
|
16
|
+
encounter?: string;
|
|
17
|
+
view?: PatientRecordView;
|
|
18
|
+
search?: AuditSearchTab;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const locationParams: Array<keyof AuditLocation> = ['patient', 'user', 'provider', 'encounter', 'view', 'search'];
|
|
22
|
+
|
|
10
23
|
/**
|
|
11
|
-
* The audit trail is a
|
|
12
|
-
*
|
|
13
|
-
* so
|
|
14
|
-
* button walks back up
|
|
24
|
+
* The audit trail is a drill-down — find the record, pick one of its encounters, then read what
|
|
25
|
+
* was entered, changed and deleted on it. Where it is drilled down to is held in the query
|
|
26
|
+
* string, so a particular encounter's audit trail can be linked to and so that the browser's back
|
|
27
|
+
* button walks back up.
|
|
15
28
|
*/
|
|
16
29
|
export default function Audit() {
|
|
17
30
|
const { t } = useTranslation();
|
|
18
31
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
19
32
|
const patientUuid = searchParams.get('patient');
|
|
33
|
+
const userUuid = searchParams.get('user');
|
|
34
|
+
const providerUuid = searchParams.get('provider');
|
|
20
35
|
const encounterUuid = searchParams.get('encounter');
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
? (
|
|
24
|
-
|
|
36
|
+
|
|
37
|
+
const oneOf = <T,>(allowed: Array<T>, value: string | null, fallback: T): T =>
|
|
38
|
+
allowed.includes(value as T) ? (value as T) : fallback;
|
|
39
|
+
const patientRecordView = oneOf(patientRecordViews, searchParams.get('view'), 'encounters');
|
|
40
|
+
const searchTab = oneOf(auditSearchTabs, searchParams.get('search'), 'patients');
|
|
25
41
|
|
|
26
42
|
const goTo = useCallback(
|
|
27
|
-
(next:
|
|
43
|
+
(next: AuditLocation) => {
|
|
28
44
|
const params = new URLSearchParams(searchParams);
|
|
29
|
-
params.delete(
|
|
30
|
-
params.delete('encounter');
|
|
31
|
-
params.delete('view');
|
|
45
|
+
locationParams.forEach((param) => params.delete(param));
|
|
32
46
|
if (next.patient) {
|
|
33
47
|
params.set('patient', next.patient);
|
|
34
48
|
}
|
|
49
|
+
if (next.user) {
|
|
50
|
+
params.set('user', next.user);
|
|
51
|
+
}
|
|
52
|
+
if (next.provider) {
|
|
53
|
+
params.set('provider', next.provider);
|
|
54
|
+
}
|
|
35
55
|
if (next.encounter) {
|
|
36
56
|
params.set('encounter', next.encounter);
|
|
37
57
|
}
|
|
38
58
|
if (next.view && next.view !== 'encounters') {
|
|
39
59
|
params.set('view', next.view);
|
|
40
60
|
}
|
|
61
|
+
if (next.search && next.search !== 'patients') {
|
|
62
|
+
params.set('search', next.search);
|
|
63
|
+
}
|
|
41
64
|
setSearchParams(params);
|
|
42
65
|
},
|
|
43
66
|
[searchParams, setSearchParams],
|
|
@@ -51,18 +74,42 @@ export default function Audit() {
|
|
|
51
74
|
{encounterUuid ? (
|
|
52
75
|
<EncounterAudit
|
|
53
76
|
encounterUuid={encounterUuid}
|
|
54
|
-
onBackToEncounters={(uuid) =>
|
|
77
|
+
onBackToEncounters={(uuid) =>
|
|
78
|
+
userUuid
|
|
79
|
+
? goTo({ user: userUuid })
|
|
80
|
+
: providerUuid
|
|
81
|
+
? goTo({ provider: providerUuid })
|
|
82
|
+
: goTo({ patient: uuid ?? patientUuid, view: patientRecordView })
|
|
83
|
+
}
|
|
84
|
+
/>
|
|
85
|
+
) : userUuid ? (
|
|
86
|
+
<UserEncounters
|
|
87
|
+
onBackToSearch={() => goTo({ search: 'users' })}
|
|
88
|
+
onSelectEncounter={(uuid) => goTo({ user: userUuid, encounter: uuid })}
|
|
89
|
+
userUuid={userUuid}
|
|
90
|
+
/>
|
|
91
|
+
) : providerUuid ? (
|
|
92
|
+
<ProviderEncounters
|
|
93
|
+
onBackToSearch={() => goTo({ search: 'providers' })}
|
|
94
|
+
onSelectEncounter={(uuid) => goTo({ provider: providerUuid, encounter: uuid })}
|
|
95
|
+
providerUuid={providerUuid}
|
|
55
96
|
/>
|
|
56
97
|
) : patientUuid ? (
|
|
57
98
|
<PatientRecord
|
|
58
|
-
onBackToSearch={() => goTo({})}
|
|
99
|
+
onBackToSearch={() => goTo({ search: 'patients' })}
|
|
59
100
|
onSelectEncounter={(uuid) => goTo({ patient: patientUuid, encounter: uuid, view: patientRecordView })}
|
|
60
101
|
onSelectView={(nextView) => goTo({ patient: patientUuid, view: nextView })}
|
|
61
102
|
patientUuid={patientUuid}
|
|
62
103
|
view={patientRecordView}
|
|
63
104
|
/>
|
|
64
105
|
) : (
|
|
65
|
-
<
|
|
106
|
+
<AuditSearch
|
|
107
|
+
onSelectPatient={(uuid) => goTo({ patient: uuid })}
|
|
108
|
+
onSelectProvider={(uuid) => goTo({ provider: uuid })}
|
|
109
|
+
onSelectTab={(tab) => goTo({ search: tab })}
|
|
110
|
+
onSelectUser={(uuid) => goTo({ user: uuid })}
|
|
111
|
+
tab={searchTab}
|
|
112
|
+
/>
|
|
66
113
|
)}
|
|
67
114
|
</div>
|
|
68
115
|
);
|