@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
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@openmrs/esm-reports-app",
3
+ "version": "4.0.6-pre.161",
4
+ "license": "MPL-2.0",
5
+ "description": "Reports admin dashboard for 03",
6
+ "browser": "dist/openmrs-esm-reports-app.js",
7
+ "main": "src/index.ts",
8
+ "source": true,
9
+ "scripts": {
10
+ "start": "openmrs develop",
11
+ "serve": "webpack serve --mode=development",
12
+ "build": "webpack --mode production",
13
+ "analyze": "webpack --mode=production --env.analyze=true",
14
+ "lint": "eslint src --ext js,jsx,ts,tsx --max-warnings=0",
15
+ "typescript": "tsc",
16
+ "test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color",
17
+ "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
18
+ "extract-translations": "i18next 'src/**/*.component.tsx' --config ../../tools/i18next-parser.config.js"
19
+ },
20
+ "browserslist": [
21
+ "extends browserslist-config-openmrs"
22
+ ],
23
+ "keywords": [
24
+ "openmrs",
25
+ "microfrontends",
26
+ "reports"
27
+ ],
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/openmrs/openmrs-esm-admin-tools.git"
31
+ },
32
+ "homepage": "https://github.com/openmrs/openmrs-esm-admin-tools#readme",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "bugs": {
37
+ "url": "https://github.com/openmrs/openmrs-esm-admin-tools/issues"
38
+ },
39
+ "dependencies": {
40
+ "@carbon/react": "^1.33.1",
41
+ "@datasert/cronjs-matcher": "^1.2.0",
42
+ "@datasert/cronjs-parser": "^1.2.0",
43
+ "cronstrue": "^2.41.0",
44
+ "dayjs": "^1.8.36",
45
+ "lodash-es": "^4.17.21",
46
+ "react-image-annotate": "^1.8.0"
47
+ },
48
+ "peerDependencies": {
49
+ "@openmrs/esm-framework": "*",
50
+ "dayjs": "1.x",
51
+ "react": "18.x",
52
+ "react-i18next": "11.x",
53
+ "react-router-dom": "6.x",
54
+ "rxjs": "6.x"
55
+ },
56
+ "stableVersion": "4.0.1"
57
+ }
@@ -0,0 +1,149 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import { take } from 'rxjs/operators';
3
+ import styles from './edit-scheduled-report-form.scss';
4
+ import SimpleCronEditor from '../simple-cron-editor/simple-cron-editor.component';
5
+ import { useReportDefinition, useReportDesigns, useReportRequest, runReportObservable } from '../reports.resource';
6
+ import ReportParameterInput from '../report-parameter-input.component';
7
+ import { Button, ButtonSet, Form, Select, SelectItem, Stack } from '@carbon/react';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { showSnackbar, useLayoutType } from '@openmrs/esm-framework';
10
+ import classNames from 'classnames';
11
+
12
+ interface EditScheduledReportForm {
13
+ reportDefinitionUuid: string;
14
+ reportRequestUuid: string;
15
+ closePanel: () => void;
16
+ }
17
+
18
+ const EditScheduledReportForm: React.FC<EditScheduledReportForm> = ({
19
+ reportDefinitionUuid,
20
+ reportRequestUuid,
21
+ closePanel,
22
+ }) => {
23
+ const { t } = useTranslation();
24
+ const isTablet = useLayoutType() === 'tablet';
25
+
26
+ const reportDefinition = useReportDefinition(reportDefinitionUuid);
27
+ const { reportDesigns } = useReportDesigns(reportDefinitionUuid);
28
+ const { reportRequest } = useReportRequest(reportRequestUuid);
29
+
30
+ const [reportParameters, setReportParameters] = useState({});
31
+ const [renderModeUuid, setRenderModeUuid] = useState();
32
+ const [initialCron, setInitialCron] = useState();
33
+ const [schedule, setSchedule] = useState('');
34
+
35
+ const [isSubmitting, setIsSubmitting] = useState(false);
36
+ const [isSubmittable, setIsSubmittable] = useState(false);
37
+ const [ignoreChanges, setIgnoreChanges] = useState(true);
38
+
39
+ useEffect(() => {
40
+ setInitialCron(reportRequest?.schedule);
41
+ setRenderModeUuid(reportRequest?.renderingMode?.argument);
42
+ }, [reportRequest]);
43
+
44
+ const handleSubmit = useCallback(
45
+ (event) => {
46
+ event.preventDefault();
47
+
48
+ setIsSubmitting(true);
49
+
50
+ const scheduleRequest = {
51
+ uuid: reportRequestUuid ? reportRequestUuid : null,
52
+ reportDefinition: {
53
+ parameterizable: {
54
+ uuid: reportDefinitionUuid,
55
+ },
56
+ parameterMappings: reportParameters,
57
+ },
58
+ renderingMode: {
59
+ argument: renderModeUuid,
60
+ },
61
+ schedule,
62
+ };
63
+
64
+ runReportObservable(scheduleRequest)
65
+ .pipe(take(1))
66
+ .subscribe(
67
+ () => {
68
+ showSnackbar({
69
+ kind: 'success',
70
+ title: t('reportScheduled', 'Report scheduled'),
71
+ subtitle: t('reportScheduledSuccessfullyMsg', 'Report scheduled successfully'),
72
+ });
73
+ closePanel();
74
+ setIsSubmitting(false);
75
+ },
76
+ () => {
77
+ showSnackbar({
78
+ kind: 'error',
79
+ title: t('reportScheduledErrorMsg', 'Failed to schedule a report'),
80
+ subtitle: t('reportScheduledErrorMsg', 'Failed to schedule a report'),
81
+ });
82
+ closePanel();
83
+ setIsSubmitting(false);
84
+ },
85
+ );
86
+ },
87
+ [closePanel, renderModeUuid, reportRequestUuid, reportParameters, schedule],
88
+ );
89
+
90
+ const handleOnChange = () => {
91
+ setIgnoreChanges((prevState) => !prevState);
92
+ };
93
+
94
+ const handleCronEditorChange = (cron: string, isValid: boolean) => {
95
+ setSchedule(isValid ? cron : '');
96
+ };
97
+
98
+ useEffect(() => {
99
+ setIsSubmittable(!!schedule && !!renderModeUuid);
100
+ }, [schedule, renderModeUuid]);
101
+
102
+ return (
103
+ <Form className={styles.desktopEditSchedule} onChange={handleOnChange} onSubmit={handleSubmit}>
104
+ <Stack gap={8} className={styles.container}>
105
+ <SimpleCronEditor initialCron={initialCron} onChange={handleCronEditorChange} />
106
+ {reportDefinition?.parameters.map((parameter) => (
107
+ <ReportParameterInput
108
+ key={`${reportDefinition.name}-${parameter.name}-param-input`}
109
+ parameter={parameter}
110
+ value={reportRequest?.parameterMappings[parameter.name]}
111
+ onChange={(parameterValue) => {
112
+ setReportParameters((state) => ({
113
+ ...state,
114
+ [parameter.name]: parameterValue,
115
+ }));
116
+ }}
117
+ />
118
+ ))}
119
+ <div className={styles.outputFormatDiv}>
120
+ <Select
121
+ className={styles.basicInputElement}
122
+ labelText={t('outputFormat', 'Output format')}
123
+ onChange={(e) => setRenderModeUuid(e.target.value)}
124
+ value={renderModeUuid}
125
+ >
126
+ <SelectItem text="" value={''} />
127
+ {reportDesigns?.map((reportDesign) => (
128
+ <SelectItem key={reportDesign.uuid} text={reportDesign.name} value={reportDesign.uuid}>
129
+ {reportDesign.name}
130
+ </SelectItem>
131
+ ))}
132
+ </Select>
133
+ </div>
134
+ </Stack>
135
+ <div className={styles.buttonsDiv}>
136
+ <ButtonSet className={classNames({ [styles.tablet]: isTablet, [styles.desktop]: !isTablet })}>
137
+ <Button className={styles.button} kind="secondary" onClick={closePanel}>
138
+ {t('cancel', 'Cancel')}
139
+ </Button>
140
+ <Button className={styles.button} disabled={isSubmitting || !isSubmittable} kind="primary" type="submit">
141
+ {t('save', 'Save')}
142
+ </Button>
143
+ </ButtonSet>
144
+ </div>
145
+ </Form>
146
+ );
147
+ };
148
+
149
+ export default EditScheduledReportForm;
@@ -0,0 +1,60 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
+
5
+ .tablet {
6
+ padding: layout.$spacing-06 layout.$spacing-05;
7
+ background-color: $ui-02;
8
+ }
9
+
10
+ .desktop {
11
+ padding: 0rem;
12
+ }
13
+
14
+ .button {
15
+ height: 4rem;
16
+ display: flex;
17
+ align-content: flex-start;
18
+ align-items: baseline;
19
+ min-width: 50%;
20
+ }
21
+
22
+ .container {
23
+ margin: layout.$spacing-05 0rem;
24
+ background-color: $ui-background;
25
+
26
+ & section {
27
+ margin: layout.$spacing-02 layout.$spacing-05 0;
28
+ }
29
+ }
30
+
31
+ .desktopEditSchedule {
32
+ background-color: $ui-background;
33
+ display: flex;
34
+ flex-direction: column;
35
+ justify-content: space-between;
36
+ }
37
+
38
+ .outputFormatDiv {
39
+ margin-bottom: 50px;
40
+ display: flex;
41
+ padding: 32px 16px 16px 16px;
42
+ flex-direction: column;
43
+ align-items: flex-start;
44
+ gap: 16px;
45
+ }
46
+
47
+ .basicInputElement {
48
+ width: 300px;
49
+ height: 30px;
50
+ margin-bottom: 30px;
51
+ }
52
+
53
+ .buttonsDiv {
54
+ margin-top: 50px;
55
+ }
56
+
57
+ .reportButton {
58
+ max-width: none !important;
59
+ width: 350px !important;
60
+ }
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import * as cronjsParser from '@datasert/cronjs-parser';
3
+ import * as cronjsMatcher from '@datasert/cronjs-matcher';
4
+ import dayjs from 'dayjs';
5
+ import utc from 'dayjs/plugin/utc';
6
+
7
+ dayjs.extend(utc);
8
+
9
+ interface NextReportExecutionProps {
10
+ schedule: string;
11
+ currentDate: Date;
12
+ }
13
+
14
+ const NextReportExecution: React.FC<NextReportExecutionProps> = ({ schedule, currentDate }) => {
15
+ const nextReportExecutionDate = (() => {
16
+ if (!schedule) {
17
+ return '';
18
+ }
19
+
20
+ const expression = cronjsParser.parse(schedule, { hasSeconds: true });
21
+ const nextExecutions = cronjsMatcher.getFutureMatches(expression, {
22
+ startAt: currentDate.toISOString(),
23
+ matchCount: 1,
24
+ });
25
+ return nextExecutions.length == 1 ? dayjs.utc(nextExecutions[0].toString()).format('YYYY-MM-DD HH:mm') : '';
26
+ })();
27
+
28
+ return <span>{nextReportExecutionDate}</span>;
29
+ };
30
+
31
+ export default NextReportExecution;
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { Header } from '@carbon/react';
3
+ import { ArrowLeft, Close } from '@carbon/react/icons';
4
+ import { useLayoutType } from '@openmrs/esm-framework';
5
+ import { closeOverlay, useOverlay } from '../hooks/useOverlay';
6
+ import styles from './overlay.scss';
7
+ import { IconButton } from '@carbon/react';
8
+ import { t } from 'i18next';
9
+ import classNames from 'classnames';
10
+
11
+ const Overlay: React.FC = () => {
12
+ const { header, component, isOverlayOpen } = useOverlay();
13
+ const layout = useLayoutType();
14
+
15
+ return (
16
+ <>
17
+ {isOverlayOpen && (
18
+ <div
19
+ className={classNames({
20
+ [styles.desktopOverlay]: layout !== 'tablet',
21
+ [styles.tabletOverlay]: layout === 'tablet',
22
+ })}
23
+ >
24
+ {layout === 'tablet' && (
25
+ <Header onClick={() => closeOverlay()} aria-label="Tablet overlay" className={styles.tabletOverlayHeader}>
26
+ <IconButton>
27
+ <ArrowLeft size={16} />
28
+ </IconButton>
29
+ <div className={styles.headerContent}>{header}</div>
30
+ </Header>
31
+ )}
32
+
33
+ {layout !== 'tablet' && (
34
+ <div className={styles.desktopHeader}>
35
+ <div className={styles.headerContent}>{header}</div>
36
+ <IconButton
37
+ className={styles.closePanelButton}
38
+ onClick={() => closeOverlay()}
39
+ kind="ghost"
40
+ label={t('close', 'Close')}
41
+ >
42
+ <Close size={16} />
43
+ </IconButton>
44
+ </div>
45
+ )}
46
+ {component}
47
+ </div>
48
+ )}
49
+ </>
50
+ );
51
+ };
52
+
53
+ export default Overlay;
@@ -0,0 +1,95 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@openmrs/esm-styleguide/src/vars' as *;
4
+ @import './../root.scss';
5
+
6
+ .desktopOverlay {
7
+ position: fixed;
8
+ top: layout.$spacing-09;
9
+ width: 700px;
10
+ right: 0;
11
+ bottom: 0;
12
+ border-left: 1px solid $text-03;
13
+ background-color: $ui-01;
14
+ overflow-y: auto;
15
+ height: calc(100vh - 3rem);
16
+ z-index: 3;
17
+ }
18
+
19
+ .desktopOverlay::after {
20
+ height: 100%;
21
+ border-left: 1px solid $text-03;
22
+ }
23
+
24
+ .tabletOverlay {
25
+ position: fixed;
26
+ top: 0;
27
+ bottom: 0;
28
+ left: 0;
29
+ right: 0;
30
+ z-index: 9999;
31
+ background-color: $ui-01;
32
+ overflow-y: scroll;
33
+ -ms-overflow-style: none;
34
+ scrollbar-width: none;
35
+ z-index: 3;
36
+
37
+ &::-webkit-scrollbar {
38
+ width: 0;
39
+ }
40
+
41
+ & > div {
42
+ margin-top: layout.$spacing-09;
43
+ }
44
+ }
45
+
46
+ .tabletOverlayHeader {
47
+ button {
48
+ @include brand-01(background-color);
49
+ }
50
+
51
+ .headerContent {
52
+ color: $ui-02;
53
+ }
54
+ }
55
+
56
+ .desktopHeader {
57
+ display: flex;
58
+ justify-content: space-between;
59
+ align-items: center;
60
+ background-color: $ui-03;
61
+ border-bottom: 1px solid $text-03;
62
+ position: sticky;
63
+ position: -webkit-sticky;
64
+ width: 100%;
65
+ z-index: 1000;
66
+ top: 0;
67
+ }
68
+
69
+ .headerContent {
70
+ @include type.type-style('heading-compact-02');
71
+ padding: 0 layout.$spacing-05;
72
+ color: $ui-05;
73
+ }
74
+
75
+ .closeButton {
76
+ background-color: $ui-background;
77
+ color: $ui-05;
78
+ fill: $ui-05;
79
+ }
80
+
81
+ /* Desktop */
82
+ :global(.omrs-breakpoint-gt-tablet) {
83
+ .overlayContent {
84
+ padding: 0 0 0 0;
85
+ overflow-y: auto;
86
+ }
87
+ }
88
+
89
+ /* Tablet */
90
+ :global(.omrs-breakpoint-lt-desktop) {
91
+ .overlayContent {
92
+ padding: 0 0 0 0;
93
+ overflow-y: auto;
94
+ }
95
+ }