@openmrs/esm-reports-app 4.0.6-pre.161
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 +36 -0
- package/README.md +23 -0
- package/dist/126.js +1 -0
- package/dist/126.js.map +1 -0
- package/dist/144.js +2 -0
- package/dist/144.js.LICENSE.txt +19 -0
- package/dist/144.js.map +1 -0
- package/dist/156.js +1 -0
- package/dist/156.js.map +1 -0
- package/dist/202.js +2 -0
- package/dist/202.js.LICENSE.txt +35 -0
- package/dist/202.js.map +1 -0
- package/dist/216.js +1 -0
- package/dist/216.js.map +1 -0
- package/dist/297.js +2 -0
- package/dist/297.js.LICENSE.txt +32 -0
- package/dist/297.js.map +1 -0
- package/dist/300.js +1 -0
- package/dist/304.js +1 -0
- package/dist/304.js.map +1 -0
- package/dist/322.js +1 -0
- package/dist/322.js.map +1 -0
- package/dist/336.js +1 -0
- package/dist/336.js.map +1 -0
- package/dist/353.js +1 -0
- package/dist/353.js.map +1 -0
- package/dist/367.js +2 -0
- package/dist/367.js.LICENSE.txt +5 -0
- package/dist/367.js.map +1 -0
- package/dist/41.js +2 -0
- package/dist/41.js.LICENSE.txt +9 -0
- package/dist/41.js.map +1 -0
- package/dist/549.js +1 -0
- package/dist/549.js.map +1 -0
- package/dist/553.js +2 -0
- package/dist/553.js.LICENSE.txt +14 -0
- package/dist/553.js.map +1 -0
- package/dist/640.js +1 -0
- package/dist/640.js.map +1 -0
- package/dist/699.js +1 -0
- package/dist/699.js.map +1 -0
- package/dist/855.js +2 -0
- package/dist/855.js.LICENSE.txt +9 -0
- package/dist/855.js.map +1 -0
- package/dist/926.js +1 -0
- package/dist/926.js.map +1 -0
- package/dist/947.js +1 -0
- package/dist/947.js.map +1 -0
- package/dist/972.js +2 -0
- package/dist/972.js.LICENSE.txt +14 -0
- package/dist/972.js.map +1 -0
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-reports-app.js +1 -0
- package/dist/openmrs-esm-reports-app.js.buildmanifest.json +657 -0
- package/dist/openmrs-esm-reports-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.js +3 -0
- package/package.json +57 -0
- package/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx +149 -0
- package/src/components/edit-scheduled-report/edit-scheduled-report-form.scss +60 -0
- package/src/components/next-report-execution.component.tsx +31 -0
- package/src/components/overlay.component.tsx +53 -0
- package/src/components/overlay.scss +95 -0
- package/src/components/overview.component.tsx +356 -0
- package/src/components/pagination-constants.ts +3 -0
- package/src/components/report-overview-button.component.tsx +27 -0
- package/src/components/report-parameter-input.component.tsx +121 -0
- package/src/components/report-schedule-description.component.tsx +21 -0
- package/src/components/report-status.component.tsx +68 -0
- package/src/components/report-statuses-constants.ts +11 -0
- package/src/components/reports.resource.tsx +224 -0
- package/src/components/reports.scss +90 -0
- package/src/components/run-report/cancel-report-modal.component.tsx +129 -0
- package/src/components/run-report/run-report-form.component.tsx +241 -0
- package/src/components/run-report/run-report-form.scss +87 -0
- package/src/components/scheduled-overview-cell-content.component.tsx +85 -0
- package/src/components/scheduled-overview.component.tsx +118 -0
- package/src/components/scheduled-report-status.component.tsx +25 -0
- package/src/components/simple-cron-editor/commons.ts +47 -0
- package/src/components/simple-cron-editor/cron-date-picker.component.tsx +67 -0
- package/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx +74 -0
- package/src/components/simple-cron-editor/cron-day-of-week-select.component.tsx +67 -0
- package/src/components/simple-cron-editor/cron-time-picker.component.tsx +60 -0
- package/src/components/simple-cron-editor/simple-cron-editor.component.tsx +342 -0
- package/src/components/simple-cron-editor/simple-cron-editor.scss +34 -0
- package/src/constants.ts +3 -0
- package/src/declarations.d.ts +6 -0
- package/src/hooks/useOverlay.tsx +45 -0
- package/src/index.ts +73 -0
- package/src/reports-link.tsx +22 -0
- package/src/reports.component.tsx +21 -0
- package/src/root.scss +15 -0
- package/src/routes.json +40 -0
- package/src/setup-tests.ts +1 -0
- package/src/types/report-definition.ts +12 -0
- package/src/types/report-design.ts +4 -0
- package/src/types/report-request.ts +8 -0
- package/src/utils/openmrs-cron-utils.ts +26 -0
- package/src/utils/time-utils.ts +43 -0
- package/translations/en.json +86 -0
- package/tsconfig.json +5 -0
- package/webpack.config.js +1 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { openmrsFetch, openmrsObservableFetch } from '@openmrs/esm-framework';
|
|
2
|
+
import type { FetchResponse } from '@openmrs/esm-framework';
|
|
3
|
+
import { type Observable } from 'rxjs';
|
|
4
|
+
import useSWR from 'swr';
|
|
5
|
+
import type { ReportDefinition } from '../types/report-definition';
|
|
6
|
+
import type { ReportDesign } from '../types/report-design';
|
|
7
|
+
import type { ReportRequest } from '../types/report-request';
|
|
8
|
+
import dayjs from 'dayjs';
|
|
9
|
+
|
|
10
|
+
interface ReportModel {
|
|
11
|
+
reportName: string;
|
|
12
|
+
status: string;
|
|
13
|
+
requestedBy: string;
|
|
14
|
+
requestedByUserUuid: string;
|
|
15
|
+
requestedOn: string;
|
|
16
|
+
outputFormat: string;
|
|
17
|
+
parameters: any;
|
|
18
|
+
id: string;
|
|
19
|
+
evaluateCompleteDatetime: string;
|
|
20
|
+
schedule: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface ScheduledReportModel {
|
|
24
|
+
reportDefinitionUuid: string;
|
|
25
|
+
reportRequestUuid: string;
|
|
26
|
+
name: string;
|
|
27
|
+
schedule: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function useLocations() {
|
|
31
|
+
const apiUrl = `/ws/rest/v1/location?tag=Login+Location`;
|
|
32
|
+
|
|
33
|
+
const { data } = useSWR<{ data: { results: Array<any> } }, Error>(apiUrl, openmrsFetch);
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
locations: data ? data?.data?.results : [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useReports(statuses: string, pageNumber: number, pageSize: number, sortBy?: string): any {
|
|
41
|
+
const reportsUrl =
|
|
42
|
+
`/ws/rest/v1/reportingrest/reportRequest?status=${statuses}&startIndex=${pageNumber}&limit=${pageSize}&totalCount=true` +
|
|
43
|
+
(sortBy ? `&sortBy=${sortBy}` : '');
|
|
44
|
+
|
|
45
|
+
const { data, error, isValidating, mutate } = useSWR<{ data: { results: Array<any>; totalCount: number } }, Error>(
|
|
46
|
+
reportsUrl,
|
|
47
|
+
openmrsFetch,
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const reports = data?.data?.results;
|
|
51
|
+
const totalCount = data?.data?.totalCount;
|
|
52
|
+
const reportsArray: Array<any> = reports ? [].concat(...reports.map((report) => mapReportResults(report))) : [];
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
reports: reportsArray,
|
|
56
|
+
reportsTotalCount: totalCount,
|
|
57
|
+
error,
|
|
58
|
+
isValidating: isValidating,
|
|
59
|
+
mutateReports: mutate,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function useReportRequest(reportRequestUuid: string): any {
|
|
64
|
+
const reportsUrl = `/ws/rest/v1/reportingrest/reportRequest/${reportRequestUuid}`;
|
|
65
|
+
|
|
66
|
+
const { data, error, isValidating, mutate } = useSWR<{ data: ReportRequest }, Error>(reportsUrl, openmrsFetch);
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
reportRequest: data?.data,
|
|
70
|
+
error,
|
|
71
|
+
isValidating: isValidating,
|
|
72
|
+
mutate,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function useScheduledReports(sortBy?: string): any {
|
|
77
|
+
const scheduledReportsUrl =
|
|
78
|
+
`/ws/rest/v1/reportingrest/reportDefinitionsWithScheduledRequests` + (sortBy ? `?sortBy=${sortBy}` : '');
|
|
79
|
+
|
|
80
|
+
const { data, error, isValidating, mutate } = useSWR<{ data: { results: Array<any> } }, Error>(
|
|
81
|
+
scheduledReportsUrl,
|
|
82
|
+
openmrsFetch,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
const scheduledReports = data?.data?.results;
|
|
86
|
+
const scheduledReportsArray: Array<any> = scheduledReports
|
|
87
|
+
? [].concat(...scheduledReports.map((report) => mapScheduledReportResults(report)))
|
|
88
|
+
: [];
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
scheduledReports: scheduledReportsArray,
|
|
92
|
+
error,
|
|
93
|
+
isValidating: isValidating,
|
|
94
|
+
mutateScheduledReports: mutate,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function useReportDefinitions() {
|
|
99
|
+
const apiUrl = `/ws/rest/v1/reportingrest/reportDefinition?v=full`;
|
|
100
|
+
|
|
101
|
+
const { data } = useSWR<{ data: { results: Array<ReportDefinition> } }, Error>(apiUrl, openmrsFetch);
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
reportDefinitions: data ? data?.data?.results : [],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function useReportDefinition(reportDefinitionUuid: string): ReportDefinition {
|
|
109
|
+
const apiUrl = `/ws/rest/v1/reportingrest/reportDefinition/${reportDefinitionUuid}?v=full`;
|
|
110
|
+
|
|
111
|
+
const { data } = useSWR<{ data: ReportDefinition }, Error>(apiUrl, openmrsFetch);
|
|
112
|
+
|
|
113
|
+
return data?.data;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function useReportDesigns(reportDefinitionUuid: string) {
|
|
117
|
+
const apiUrl = `/ws/rest/v1/reportingrest/reportDesign?reportDefinitionUuid=${reportDefinitionUuid}`;
|
|
118
|
+
|
|
119
|
+
const { data, error, isValidating, mutate } = useSWR<{ data: { results: ReportDesign[] } }, Error>(
|
|
120
|
+
reportDefinitionUuid ? apiUrl : null,
|
|
121
|
+
openmrsFetch,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
reportDesigns: data?.data.results,
|
|
126
|
+
error,
|
|
127
|
+
isValidating: isValidating,
|
|
128
|
+
mutateReportDesigns: mutate,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function runReportObservable(payload: any): Observable<FetchResponse<any>> {
|
|
133
|
+
const abortController = new AbortController();
|
|
134
|
+
return openmrsObservableFetch(`/ws/rest/v1/reportingrest/reportRequest`, {
|
|
135
|
+
signal: abortController.signal,
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: {
|
|
138
|
+
'Content-type': 'application/json',
|
|
139
|
+
},
|
|
140
|
+
body: payload,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export async function cancelReportRequest(reportRequestUuid: string) {
|
|
145
|
+
const apiUrl = `/ws/rest/v1/reportingrest/reportRequest/${reportRequestUuid}`;
|
|
146
|
+
|
|
147
|
+
return openmrsFetch(apiUrl, {
|
|
148
|
+
method: 'DELETE',
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function preserveReport(reportRequestUuid: string) {
|
|
153
|
+
const apiUrl = `/ws/rest/v1/reportingrest/saveReport?reportRequestUuid=${reportRequestUuid}`;
|
|
154
|
+
|
|
155
|
+
return openmrsFetch(apiUrl, {
|
|
156
|
+
method: 'POST',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function downloadReport(reportRequestUuid: string) {
|
|
161
|
+
const apiUrl = `/ws/rest/v1/reportingrest/downloadReport?reportRequestUuid=${reportRequestUuid}`;
|
|
162
|
+
|
|
163
|
+
const { data } = await openmrsFetch<any>(apiUrl);
|
|
164
|
+
|
|
165
|
+
return data;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export async function downloadMultipleReports(reportRequestUuids: string[]) {
|
|
169
|
+
const apiUrl = `/ws/rest/v1/reportingrest/downloadMultipleReports?reportRequestUuids=${reportRequestUuids}`;
|
|
170
|
+
|
|
171
|
+
const { data } = await openmrsFetch<any>(apiUrl);
|
|
172
|
+
|
|
173
|
+
return data;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function mapReportResults(data: any): ReportModel {
|
|
177
|
+
return {
|
|
178
|
+
id: data.uuid,
|
|
179
|
+
reportName: data.parameterizable.name,
|
|
180
|
+
status: data.status,
|
|
181
|
+
requestedBy: data.requestedBy.person.display,
|
|
182
|
+
requestedByUserUuid: data.requestedBy.uuid,
|
|
183
|
+
requestedOn: dayjs(data.requestDate).format('YYYY-MM-DD HH:mm'),
|
|
184
|
+
outputFormat: data.renderingMode.label,
|
|
185
|
+
parameters: convertParametersToString(data),
|
|
186
|
+
evaluateCompleteDatetime: data.evaluateCompleteDatetime
|
|
187
|
+
? dayjs(data.evaluateCompleteDatetime).format('YYYY-MM-DD HH:mm')
|
|
188
|
+
: undefined,
|
|
189
|
+
schedule: data.schedule,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function mapScheduledReportResults(data: any): ScheduledReportModel {
|
|
194
|
+
return {
|
|
195
|
+
reportDefinitionUuid: data.reportDefinition.uuid,
|
|
196
|
+
reportRequestUuid: data.scheduledRequests?.[0]?.uuid,
|
|
197
|
+
name: data.reportDefinition.display,
|
|
198
|
+
schedule: data.scheduledRequests?.[0]?.schedule,
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function convertParametersToString(data: any): string {
|
|
203
|
+
let finalString = '';
|
|
204
|
+
const parameters = data.parameterizable.parameters;
|
|
205
|
+
if (parameters.length > 0) {
|
|
206
|
+
parameters.forEach((parameter) => {
|
|
207
|
+
let value = data.parameterMappings[parameter.name];
|
|
208
|
+
if (parameter.type === 'java.util.Date') {
|
|
209
|
+
value = dayjs(value).format('YYYY-MM-DD');
|
|
210
|
+
} else if (parameter.type === 'org.openmrs.Location') {
|
|
211
|
+
value = value?.display;
|
|
212
|
+
}
|
|
213
|
+
finalString = finalString + parameter.label + ': ' + value + ', ';
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
finalString = finalString.trim();
|
|
217
|
+
|
|
218
|
+
if (finalString.charAt(finalString.length - 1) === ',') {
|
|
219
|
+
finalString = finalString.slice(0, -1);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return finalString;
|
|
224
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
.statusIcon {
|
|
2
|
+
margin-bottom: -2px;
|
|
3
|
+
margin-right: 5px;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.failedIcon {
|
|
7
|
+
color: red;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.successIcon {
|
|
11
|
+
color: green;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.runningIcon {
|
|
15
|
+
display: inline-block;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.actionButon {
|
|
19
|
+
padding-left: 0px;
|
|
20
|
+
color: inherit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.actionButtonIcon {
|
|
24
|
+
margin-left: 5px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.mainActionButton {
|
|
28
|
+
margin-top: 25px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.mainPanelDiv {
|
|
32
|
+
height: 130px;
|
|
33
|
+
background-color: white;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.emptyButton {
|
|
37
|
+
width: 100px;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.actionButtonsWrapperDiv {
|
|
41
|
+
width: 110px;
|
|
42
|
+
display: inline-block;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.mainActionButtonsDiv {
|
|
46
|
+
text-align: right;
|
|
47
|
+
height: 48px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.reportsLabelDiv {
|
|
51
|
+
margin-left: 20px;
|
|
52
|
+
padding-top: 20px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.breadcrumbsSlot {
|
|
56
|
+
grid-row: 1 / 2;
|
|
57
|
+
grid-column: 1 / 2;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.downloadReportsVisible {
|
|
61
|
+
display: inline-block;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.downloadReportsHidden {
|
|
65
|
+
display: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.breadcrumb {
|
|
69
|
+
margin-top: -30px;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.rowCellEven {
|
|
73
|
+
background-color: #FFFFFF !important;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.rowCellOdd {
|
|
77
|
+
background-color: #F4F4F4 !important;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.scheduledStatusText {
|
|
81
|
+
color: green;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.scheduleCompletedStatusText {
|
|
85
|
+
color: red;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.notScheduledStatusText {
|
|
89
|
+
color: red;
|
|
90
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { ModalBody, Button, ModalFooter, ModalHeader, InlineLoading } from '@carbon/react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { cancelReportRequest } from '../reports.resource';
|
|
5
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
6
|
+
import { mutate } from 'swr';
|
|
7
|
+
import {
|
|
8
|
+
PROCESSING_REPORT_STATUSES,
|
|
9
|
+
RAN_REPORT_STATUSES,
|
|
10
|
+
SCHEDULED_REPORT_STATUSES,
|
|
11
|
+
} from '../report-statuses-constants';
|
|
12
|
+
|
|
13
|
+
interface CancelReportModalProps {
|
|
14
|
+
closeModal: () => void;
|
|
15
|
+
reportRequestUuid: string;
|
|
16
|
+
modalType: ModalType;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type ModalType = 'delete' | 'cancel' | 'schedule';
|
|
20
|
+
|
|
21
|
+
const CancelReportModal: React.FC<CancelReportModalProps> = ({ closeModal, reportRequestUuid, modalType }) => {
|
|
22
|
+
const { t } = useTranslation();
|
|
23
|
+
const [isCanceling, setIsCanceling] = useState(false);
|
|
24
|
+
|
|
25
|
+
const handleCancel = useCallback(async () => {
|
|
26
|
+
try {
|
|
27
|
+
setIsCanceling(true);
|
|
28
|
+
await cancelReportRequest(reportRequestUuid);
|
|
29
|
+
callMutates(modalType);
|
|
30
|
+
closeModal();
|
|
31
|
+
showSnackbar({
|
|
32
|
+
kind: 'success',
|
|
33
|
+
title: getModalTitleByType(modalType),
|
|
34
|
+
subtitle: getSuccessToastMessageByType(modalType),
|
|
35
|
+
});
|
|
36
|
+
} catch (error) {
|
|
37
|
+
showSnackbar({
|
|
38
|
+
kind: 'error',
|
|
39
|
+
title: getModalTitleByType(modalType),
|
|
40
|
+
subtitle: getFailedToastMessageByType(modalType),
|
|
41
|
+
});
|
|
42
|
+
} finally {
|
|
43
|
+
setIsCanceling(false);
|
|
44
|
+
}
|
|
45
|
+
}, [closeModal]);
|
|
46
|
+
|
|
47
|
+
const callMutates = (modalType: ModalType) => {
|
|
48
|
+
let baseUrl = '/ws/rest/v1/reportingrest/reportRequest?status=';
|
|
49
|
+
if (modalType === 'delete') {
|
|
50
|
+
mutate(baseUrl + RAN_REPORT_STATUSES.join(','));
|
|
51
|
+
} else if (modalType === 'cancel') {
|
|
52
|
+
mutate(baseUrl + RAN_REPORT_STATUSES.join(','));
|
|
53
|
+
mutate(baseUrl + PROCESSING_REPORT_STATUSES.join(','));
|
|
54
|
+
} else if (modalType === 'schedule') {
|
|
55
|
+
mutate(baseUrl + SCHEDULED_REPORT_STATUSES.join(',') + '&sortBy=name');
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const getModalTitleByType = (modalType: ModalType) => {
|
|
60
|
+
if (modalType === 'delete') {
|
|
61
|
+
return t('deleteReport', 'Delete report');
|
|
62
|
+
} else if (modalType === 'cancel') {
|
|
63
|
+
return t('cancelReport', 'Cancel report');
|
|
64
|
+
} else if (modalType === 'schedule') {
|
|
65
|
+
return t('scheduleReport', 'Schedule report');
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const getModalBodyByType = (modalType: ModalType) => {
|
|
70
|
+
if (modalType === 'delete') {
|
|
71
|
+
return t('deleteReportModalText', 'Are you sure you want to delete this report?');
|
|
72
|
+
} else if (modalType === 'cancel') {
|
|
73
|
+
return t('cancelReportModalText', 'Are you sure you want to cancel this report?');
|
|
74
|
+
} else if (modalType === 'schedule') {
|
|
75
|
+
return t('deleteReportScheduleModalText', 'Are you sure you want to delete this schedule?');
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const getSuccessToastMessageByType = (modalType: ModalType) => {
|
|
80
|
+
if (modalType === 'delete') {
|
|
81
|
+
return t('reportDeletedSuccessfully', 'Report deleted successfully');
|
|
82
|
+
} else if (modalType === 'cancel') {
|
|
83
|
+
return t('reportCancelledSuccessfully', 'Report cancelled successfully');
|
|
84
|
+
} else if (modalType === 'schedule') {
|
|
85
|
+
return t('reportScheduleDeletedSuccessfully', 'Report schedule deleted successfully');
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getFailedToastMessageByType = (modalType: ModalType) => {
|
|
90
|
+
if (modalType === 'delete') {
|
|
91
|
+
return t('reportDeletingErrorMsg', 'Error during report deleting');
|
|
92
|
+
} else if (modalType === 'cancel') {
|
|
93
|
+
return t('reportCancelingErrorMsg', 'Error during report canceling');
|
|
94
|
+
} else if (modalType === 'schedule') {
|
|
95
|
+
return t('reportScheduleDeletingErrorMsg', 'Error during report schedule deleting');
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const getLoadingMessageByType = (modalType: ModalType) => {
|
|
100
|
+
if (modalType === 'delete' || modalType === 'schedule') {
|
|
101
|
+
return t('deleting', 'Deleting');
|
|
102
|
+
} else if (modalType === 'cancel') {
|
|
103
|
+
return t('canceling', 'Canceling');
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div>
|
|
109
|
+
<ModalHeader closeModal={closeModal} title={getModalTitleByType(modalType)} />
|
|
110
|
+
<ModalBody>
|
|
111
|
+
<p>{getModalBodyByType(modalType)}</p>
|
|
112
|
+
</ModalBody>
|
|
113
|
+
<ModalFooter>
|
|
114
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
115
|
+
{t('no', 'No')}
|
|
116
|
+
</Button>
|
|
117
|
+
<Button kind="danger" onClick={handleCancel} disabled={isCanceling}>
|
|
118
|
+
{isCanceling ? (
|
|
119
|
+
<InlineLoading description={getLoadingMessageByType(modalType) + '...'} />
|
|
120
|
+
) : (
|
|
121
|
+
<span>{t('yes', 'Yes')}</span>
|
|
122
|
+
)}
|
|
123
|
+
</Button>
|
|
124
|
+
</ModalFooter>
|
|
125
|
+
</div>
|
|
126
|
+
);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export default CancelReportModal;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import React, { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import styles from './run-report-form.scss';
|
|
4
|
+
import { useLocations, useReportDefinitions, useReportDesigns, runReportObservable } from '../reports.resource';
|
|
5
|
+
import { closeOverlay } from '../../hooks/useOverlay';
|
|
6
|
+
import { Button, ButtonSet, DatePicker, DatePickerInput, Form, Select, SelectItem, TextInput } from '@carbon/react';
|
|
7
|
+
import { showSnackbar, useLayoutType } from '@openmrs/esm-framework';
|
|
8
|
+
import { take } from 'rxjs/operators';
|
|
9
|
+
import classNames from 'classnames';
|
|
10
|
+
|
|
11
|
+
interface RunReportForm {
|
|
12
|
+
closePanel: () => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const RunReportForm: React.FC<RunReportForm> = ({ closePanel }) => {
|
|
16
|
+
const { t } = useTranslation();
|
|
17
|
+
const [reportUuid, setReportUuid] = useState('');
|
|
18
|
+
const [renderModeUuid, setRenderModeUuid] = useState('');
|
|
19
|
+
const [currentReport, setCurrentReport] = useState(null);
|
|
20
|
+
const [reportParameters, setReportParameters] = useState({});
|
|
21
|
+
const [isFormValid, setIsFormValid] = useState(false);
|
|
22
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
23
|
+
const isTablet = useLayoutType() === 'tablet';
|
|
24
|
+
|
|
25
|
+
const { reportDesigns, mutateReportDesigns } = useReportDesigns(reportUuid);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
mutateReportDesigns();
|
|
29
|
+
}, [reportUuid]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const paramTypes = currentReport?.parameters.map((param) => param.type);
|
|
33
|
+
const isAnyNotSupportedType = !paramTypes?.every((paramType) => supportedParameterTypes.includes(paramType));
|
|
34
|
+
const allParametersNotEmpty = currentReport?.parameters.every(
|
|
35
|
+
(parameter) => !!reportParameters[parameter.name] && reportParameters[parameter.name] !== 'Invalid Date',
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
if (!isAnyNotSupportedType && allParametersNotEmpty && reportUuid !== '' && renderModeUuid !== '') {
|
|
39
|
+
setIsFormValid(true);
|
|
40
|
+
} else {
|
|
41
|
+
setIsFormValid(false);
|
|
42
|
+
}
|
|
43
|
+
}, [reportParameters, reportUuid, renderModeUuid]);
|
|
44
|
+
|
|
45
|
+
const supportedParameterTypes = ['java.util.Date', 'java.lang.String', 'java.lang.Integer', 'org.openmrs.Location'];
|
|
46
|
+
|
|
47
|
+
const { reportDefinitions } = useReportDefinitions();
|
|
48
|
+
const { locations } = useLocations();
|
|
49
|
+
|
|
50
|
+
function renderParameterElementBasedOnType(parameter: any) {
|
|
51
|
+
switch (parameter.type) {
|
|
52
|
+
case 'java.util.Date':
|
|
53
|
+
return (
|
|
54
|
+
<div className={styles.runReportInnerDivElement}>
|
|
55
|
+
<DatePicker
|
|
56
|
+
datePickerType="single"
|
|
57
|
+
name={parameter.name}
|
|
58
|
+
onChange={([date]) => handleOnDateChange(parameter.name, date)}
|
|
59
|
+
dateFormat="Y-m-d"
|
|
60
|
+
className={styles.datePicker}
|
|
61
|
+
>
|
|
62
|
+
<DatePickerInput id={parameter.name} name={parameter.name} labelText={parameter.label} type="date" />
|
|
63
|
+
</DatePicker>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
case 'java.lang.String':
|
|
67
|
+
case 'java.lang.Integer':
|
|
68
|
+
return (
|
|
69
|
+
<div className={styles.runReportInnerDivElement}>
|
|
70
|
+
<TextInput
|
|
71
|
+
id={parameter.name}
|
|
72
|
+
name={parameter.name}
|
|
73
|
+
labelText={parameter.label}
|
|
74
|
+
className={styles.basicInputElement}
|
|
75
|
+
onChange={(e) => handleOnChange(e)}
|
|
76
|
+
value={reportParameters[parameter.name] ?? ''}
|
|
77
|
+
/>
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
case 'org.openmrs.Location':
|
|
81
|
+
return (
|
|
82
|
+
<div className={styles.runReportInnerDivElement}>
|
|
83
|
+
<Select
|
|
84
|
+
id={parameter.name}
|
|
85
|
+
name={parameter.name}
|
|
86
|
+
labelText={parameter.label}
|
|
87
|
+
className={styles.basicInputElement}
|
|
88
|
+
onChange={(e) => handleOnChange(e)}
|
|
89
|
+
value={reportParameters[parameter.name] ?? ''}
|
|
90
|
+
>
|
|
91
|
+
<SelectItem value="" />
|
|
92
|
+
{locations?.map((location) => (
|
|
93
|
+
<SelectItem key={location.uuid} text={location.display} value={location.uuid}>
|
|
94
|
+
{location.display}
|
|
95
|
+
</SelectItem>
|
|
96
|
+
))}
|
|
97
|
+
</Select>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
default:
|
|
101
|
+
return (
|
|
102
|
+
<div className={styles.runReportInnerDivElement}>
|
|
103
|
+
<span className={styles.unknownParameterTypeSpan}>
|
|
104
|
+
{`Unknown parameter type: ${parameter.type} for parameter: ${parameter.label}`}
|
|
105
|
+
</span>
|
|
106
|
+
</div>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handleOnChange(event) {
|
|
112
|
+
const key = event.target.name;
|
|
113
|
+
let value = null;
|
|
114
|
+
if (event.target.type == 'checkbox') {
|
|
115
|
+
value = event.target.checked;
|
|
116
|
+
} else {
|
|
117
|
+
value = event.target.value;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setReportParameters((state) => ({ ...state, [key]: value }));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function handleOnDateChange(fieldName, dateValue) {
|
|
124
|
+
const date = new Date(dateValue).toLocaleDateString();
|
|
125
|
+
setReportParameters((state) => ({ ...state, [fieldName]: date }));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const handleSubmit = useCallback(
|
|
129
|
+
(event) => {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
|
|
132
|
+
setIsSubmitting(true);
|
|
133
|
+
|
|
134
|
+
const reportRequest = {
|
|
135
|
+
uuid: null,
|
|
136
|
+
reportDefinition: {
|
|
137
|
+
parameterizable: {
|
|
138
|
+
uuid: reportUuid,
|
|
139
|
+
},
|
|
140
|
+
parameterMappings: reportParameters,
|
|
141
|
+
},
|
|
142
|
+
renderingMode: {
|
|
143
|
+
argument: renderModeUuid,
|
|
144
|
+
},
|
|
145
|
+
schedule: null,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
runReportObservable(reportRequest)
|
|
149
|
+
.pipe(take(1))
|
|
150
|
+
.subscribe(
|
|
151
|
+
() => {
|
|
152
|
+
// delayed handling because runReport returns before new reports is accessible via GET
|
|
153
|
+
setTimeout(() => {
|
|
154
|
+
showSnackbar({
|
|
155
|
+
kind: 'success',
|
|
156
|
+
title: t('reportRunning', 'Report running'),
|
|
157
|
+
subtitle: t('reportRanSuccessfullyMsg', 'Report ran successfully'),
|
|
158
|
+
});
|
|
159
|
+
closePanel();
|
|
160
|
+
setIsSubmitting(false);
|
|
161
|
+
}, 500);
|
|
162
|
+
},
|
|
163
|
+
(error) => {
|
|
164
|
+
console.error(error);
|
|
165
|
+
showSnackbar({
|
|
166
|
+
kind: 'error',
|
|
167
|
+
title: t('reportRunningErrorMsg', 'Error while running the report'),
|
|
168
|
+
subtitle: t('reportRunningErrorMsg', 'Error while running the report'),
|
|
169
|
+
});
|
|
170
|
+
setIsSubmitting(false);
|
|
171
|
+
},
|
|
172
|
+
);
|
|
173
|
+
},
|
|
174
|
+
[reportUuid, renderModeUuid, reportParameters],
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<Form className={styles.desktopRunReport} onSubmit={handleSubmit}>
|
|
179
|
+
<div className={styles.runReportInnerDivElement}>
|
|
180
|
+
<Select
|
|
181
|
+
id="select-report"
|
|
182
|
+
className={styles.basicInputElement}
|
|
183
|
+
labelText={t('selectReportLabel', 'Report')}
|
|
184
|
+
onChange={(e) => {
|
|
185
|
+
setReportUuid(e.target.value);
|
|
186
|
+
setRenderModeUuid('');
|
|
187
|
+
setCurrentReport(reportDefinitions.find((reportDefinition) => reportDefinition.uuid === e.target.value));
|
|
188
|
+
setReportParameters({});
|
|
189
|
+
}}
|
|
190
|
+
value={reportUuid}
|
|
191
|
+
>
|
|
192
|
+
<SelectItem text="" value={''} />
|
|
193
|
+
{reportDefinitions?.map((reportDefinition) => (
|
|
194
|
+
<SelectItem key={reportDefinition.uuid} text={reportDefinition.name} value={reportDefinition.uuid}>
|
|
195
|
+
{reportDefinition.name}
|
|
196
|
+
</SelectItem>
|
|
197
|
+
))}
|
|
198
|
+
</Select>
|
|
199
|
+
</div>
|
|
200
|
+
<div id="reportParametersDiv">
|
|
201
|
+
{currentReport?.parameters?.map((parameter) => (
|
|
202
|
+
<div key={`param-${parameter.name}`}>{renderParameterElementBasedOnType(parameter)}</div>
|
|
203
|
+
))}
|
|
204
|
+
</div>
|
|
205
|
+
<div className={styles.outputFormatDiv}>
|
|
206
|
+
<Select
|
|
207
|
+
id="output-format-select"
|
|
208
|
+
className={styles.basicInputElement}
|
|
209
|
+
labelText={t('outputFormat', 'Output format')}
|
|
210
|
+
onChange={(e) => setRenderModeUuid(e.target.value)}
|
|
211
|
+
value={renderModeUuid}
|
|
212
|
+
>
|
|
213
|
+
<SelectItem text="" value={''} />
|
|
214
|
+
{reportDesigns?.map((reportDesign) => (
|
|
215
|
+
<SelectItem key={reportDesign.uuid} text={reportDesign.name} value={reportDesign.uuid}>
|
|
216
|
+
{reportDesign.name}
|
|
217
|
+
</SelectItem>
|
|
218
|
+
))}
|
|
219
|
+
</Select>
|
|
220
|
+
</div>
|
|
221
|
+
<div className={styles.buttonsDiv}>
|
|
222
|
+
<ButtonSet className={classNames({ [styles.tablet]: isTablet, [styles.desktop]: !isTablet })}>
|
|
223
|
+
<Button onClick={closeOverlay} kind="secondary" size="xl" className={styles.reportButton}>
|
|
224
|
+
{t('cancel', 'Cancel')}
|
|
225
|
+
</Button>
|
|
226
|
+
<Button
|
|
227
|
+
disabled={!isFormValid || isSubmitting}
|
|
228
|
+
size="xl"
|
|
229
|
+
className={styles.reportButton}
|
|
230
|
+
kind="primary"
|
|
231
|
+
type="submit"
|
|
232
|
+
>
|
|
233
|
+
{t('run', 'Run')}
|
|
234
|
+
</Button>
|
|
235
|
+
</ButtonSet>
|
|
236
|
+
</div>
|
|
237
|
+
</Form>
|
|
238
|
+
);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
export default RunReportForm;
|