@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.
Files changed (103) hide show
  1. package/.turbo/turbo-build.log +36 -0
  2. package/README.md +23 -0
  3. package/dist/126.js +1 -0
  4. package/dist/126.js.map +1 -0
  5. package/dist/144.js +2 -0
  6. package/dist/144.js.LICENSE.txt +19 -0
  7. package/dist/144.js.map +1 -0
  8. package/dist/156.js +1 -0
  9. package/dist/156.js.map +1 -0
  10. package/dist/202.js +2 -0
  11. package/dist/202.js.LICENSE.txt +35 -0
  12. package/dist/202.js.map +1 -0
  13. package/dist/216.js +1 -0
  14. package/dist/216.js.map +1 -0
  15. package/dist/297.js +2 -0
  16. package/dist/297.js.LICENSE.txt +32 -0
  17. package/dist/297.js.map +1 -0
  18. package/dist/300.js +1 -0
  19. package/dist/304.js +1 -0
  20. package/dist/304.js.map +1 -0
  21. package/dist/322.js +1 -0
  22. package/dist/322.js.map +1 -0
  23. package/dist/336.js +1 -0
  24. package/dist/336.js.map +1 -0
  25. package/dist/353.js +1 -0
  26. package/dist/353.js.map +1 -0
  27. package/dist/367.js +2 -0
  28. package/dist/367.js.LICENSE.txt +5 -0
  29. package/dist/367.js.map +1 -0
  30. package/dist/41.js +2 -0
  31. package/dist/41.js.LICENSE.txt +9 -0
  32. package/dist/41.js.map +1 -0
  33. package/dist/549.js +1 -0
  34. package/dist/549.js.map +1 -0
  35. package/dist/553.js +2 -0
  36. package/dist/553.js.LICENSE.txt +14 -0
  37. package/dist/553.js.map +1 -0
  38. package/dist/640.js +1 -0
  39. package/dist/640.js.map +1 -0
  40. package/dist/699.js +1 -0
  41. package/dist/699.js.map +1 -0
  42. package/dist/855.js +2 -0
  43. package/dist/855.js.LICENSE.txt +9 -0
  44. package/dist/855.js.map +1 -0
  45. package/dist/926.js +1 -0
  46. package/dist/926.js.map +1 -0
  47. package/dist/947.js +1 -0
  48. package/dist/947.js.map +1 -0
  49. package/dist/972.js +2 -0
  50. package/dist/972.js.LICENSE.txt +14 -0
  51. package/dist/972.js.map +1 -0
  52. package/dist/main.js +1 -0
  53. package/dist/main.js.map +1 -0
  54. package/dist/openmrs-esm-reports-app.js +1 -0
  55. package/dist/openmrs-esm-reports-app.js.buildmanifest.json +657 -0
  56. package/dist/openmrs-esm-reports-app.js.map +1 -0
  57. package/dist/routes.json +1 -0
  58. package/jest.config.js +3 -0
  59. package/package.json +57 -0
  60. package/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx +149 -0
  61. package/src/components/edit-scheduled-report/edit-scheduled-report-form.scss +60 -0
  62. package/src/components/next-report-execution.component.tsx +31 -0
  63. package/src/components/overlay.component.tsx +53 -0
  64. package/src/components/overlay.scss +95 -0
  65. package/src/components/overview.component.tsx +356 -0
  66. package/src/components/pagination-constants.ts +3 -0
  67. package/src/components/report-overview-button.component.tsx +27 -0
  68. package/src/components/report-parameter-input.component.tsx +121 -0
  69. package/src/components/report-schedule-description.component.tsx +21 -0
  70. package/src/components/report-status.component.tsx +68 -0
  71. package/src/components/report-statuses-constants.ts +11 -0
  72. package/src/components/reports.resource.tsx +224 -0
  73. package/src/components/reports.scss +90 -0
  74. package/src/components/run-report/cancel-report-modal.component.tsx +129 -0
  75. package/src/components/run-report/run-report-form.component.tsx +241 -0
  76. package/src/components/run-report/run-report-form.scss +87 -0
  77. package/src/components/scheduled-overview-cell-content.component.tsx +85 -0
  78. package/src/components/scheduled-overview.component.tsx +118 -0
  79. package/src/components/scheduled-report-status.component.tsx +25 -0
  80. package/src/components/simple-cron-editor/commons.ts +47 -0
  81. package/src/components/simple-cron-editor/cron-date-picker.component.tsx +67 -0
  82. package/src/components/simple-cron-editor/cron-day-of-month-select.component.tsx +74 -0
  83. package/src/components/simple-cron-editor/cron-day-of-week-select.component.tsx +67 -0
  84. package/src/components/simple-cron-editor/cron-time-picker.component.tsx +60 -0
  85. package/src/components/simple-cron-editor/simple-cron-editor.component.tsx +342 -0
  86. package/src/components/simple-cron-editor/simple-cron-editor.scss +34 -0
  87. package/src/constants.ts +3 -0
  88. package/src/declarations.d.ts +6 -0
  89. package/src/hooks/useOverlay.tsx +45 -0
  90. package/src/index.ts +73 -0
  91. package/src/reports-link.tsx +22 -0
  92. package/src/reports.component.tsx +21 -0
  93. package/src/root.scss +15 -0
  94. package/src/routes.json +40 -0
  95. package/src/setup-tests.ts +1 -0
  96. package/src/types/report-definition.ts +12 -0
  97. package/src/types/report-design.ts +4 -0
  98. package/src/types/report-request.ts +8 -0
  99. package/src/utils/openmrs-cron-utils.ts +26 -0
  100. package/src/utils/time-utils.ts +43 -0
  101. package/translations/en.json +86 -0
  102. package/tsconfig.json +5 -0
  103. 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;