-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Reports overview - Report schedules overview
- Loading branch information
Showing
53 changed files
with
11,171 additions
and
193 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
![Node.js CI](https://github.com/openmrs/openmrs-esm-template-app/workflows/Node.js%20CI/badge.svg) | ||
|
||
# Reports Module | ||
|
||
The `openmrs-esm-reports-app` is a package which provides Report admin pages: | ||
- An overview of Report execution history included currently queued reports, with possibilities to execute specific report | ||
, preserve, download and delete completed execution | ||
- An overview of an execution schedule with possibilities to view, edit and delete a schedule | ||
|
||
The pages are available in the app's main menu under Reports entry. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const rootConfig = require('../../jest.config.js'); | ||
|
||
module.exports = rootConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
{ | ||
"name": "@openmrs/esm-reports-app", | ||
"version": "4.0.1", | ||
"license": "MPL-2.0", | ||
"description": "Reports Admin page for OpenMRS", | ||
"browser": "dist/openmrs-esm-reports-app.js", | ||
"main": "src/index.ts", | ||
"source": true, | ||
"scripts": { | ||
"start": "openmrs develop", | ||
"serve": "webpack serve --mode=development", | ||
"build": "webpack --mode production", | ||
"analyze": "webpack --mode=production --env.analyze=true", | ||
"lint": "eslint src --ext js,jsx,ts,tsx", | ||
"typescript": "tsc", | ||
"test": "cross-env TZ=UTC jest --config jest.config.js --verbose false --passWithNoTests --color", | ||
"test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color", | ||
"extract-translations": "i18next 'src/**/*.component.tsx' --config ../../tools/i18next-parser.config.js" | ||
}, | ||
"browserslist": [ | ||
"extends browserslist-config-openmrs" | ||
], | ||
"keywords": [ | ||
"openmrs", | ||
"microfrontends", | ||
"reports" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/openmrs/openmrs-esm-admin-tools.git" | ||
}, | ||
"homepage": "https://github.com/openmrs/openmrs-esm-admin-tools#readme", | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/openmrs/openmrs-esm-admin-tools/issues" | ||
}, | ||
"dependencies": { | ||
"@carbon/react": "^1.33.1", | ||
"@datasert/cronjs-matcher": "^1.2.0", | ||
"@datasert/cronjs-parser": "^1.2.0", | ||
"cronstrue": "^2.41.0", | ||
"dayjs": "^1.8.36", | ||
"lodash-es": "^4.17.21", | ||
"react-image-annotate": "^1.8.0" | ||
}, | ||
"peerDependencies": { | ||
"@openmrs/esm-framework": "*", | ||
"dayjs": "1.x", | ||
"react": "18.x", | ||
"react-i18next": "11.x", | ||
"react-router-dom": "6.x", | ||
"rxjs": "6.x" | ||
}, | ||
"packageManager": "[email protected]" | ||
} |
153 changes: 153 additions & 0 deletions
153
...reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
import React, { useCallback, useEffect, useState } from 'react'; | ||
import { first } from 'rxjs/operators'; | ||
import styles from './edit-scheduled-report-form.scss'; | ||
import SimpleCronEditor from '../simple-cron-editor/simple-cron-editor.component'; | ||
import { | ||
useReportDefinition, | ||
useReportDesigns, | ||
useReportRequest, | ||
runReportObservable, | ||
RunReportRequest, | ||
} from '../reports.resource'; | ||
import ReportParameterInput from '../report-parameter-input.component'; | ||
import { Button, ButtonSet, Form, Select, SelectItem, Stack } from '@carbon/react'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { showToast, useLayoutType } from '@openmrs/esm-framework'; | ||
|
||
interface EditScheduledReportForm { | ||
reportDefinitionUuid: string; | ||
reportRequestUuid: string; | ||
closePanel: () => void; | ||
} | ||
|
||
const EditScheduledReportForm: React.FC<EditScheduledReportForm> = ({ | ||
reportDefinitionUuid, | ||
reportRequestUuid, | ||
closePanel, | ||
}) => { | ||
const { t } = useTranslation(); | ||
const isTablet = useLayoutType() === 'tablet'; | ||
|
||
const reportDefinition = useReportDefinition(reportDefinitionUuid); | ||
const { reportDesigns } = useReportDesigns(reportDefinitionUuid); | ||
const { reportRequest } = useReportRequest(reportRequestUuid); | ||
|
||
const [reportParameters, setReportParameters] = useState({}); | ||
const [renderModeUuid, setRenderModeUuid] = useState<string>(); | ||
const [initialCron, setInitialCron] = useState<string>(); | ||
const [schedule, setSchedule] = useState<string>(); | ||
|
||
const [isSubmitting, setIsSubmitting] = useState(false); | ||
const [isSubmittable, setIsSubmittable] = useState(false); | ||
const [ignoreChanges, setIgnoreChanges] = useState(true); | ||
|
||
useEffect(() => { | ||
setInitialCron(reportRequest?.schedule); | ||
setRenderModeUuid(reportRequest?.renderingMode?.argument); | ||
}, [reportRequest]); | ||
|
||
const handleSubmit = useCallback( | ||
(event) => { | ||
event.preventDefault(); | ||
|
||
setIsSubmitting(true); | ||
|
||
const runReportRequest: RunReportRequest = { | ||
existingRequestUuid: reportRequestUuid, | ||
reportDefinitionUuid, | ||
renderModeUuid, | ||
reportParameters, | ||
schedule, | ||
}; | ||
|
||
const abortController = new AbortController(); | ||
runReportObservable(runReportRequest, abortController) | ||
.pipe(first()) | ||
.subscribe( | ||
() => { | ||
showToast({ | ||
critical: true, | ||
kind: 'success', | ||
title: t('reportScheduled', 'Report scheduled'), | ||
description: t('reportScheduledSuccessfullyMsg', 'Report scheduled successfully'), | ||
}); | ||
closePanel(); | ||
setIsSubmitting(false); | ||
}, | ||
(error) => { | ||
console.error(error); | ||
showToast({ | ||
critical: true, | ||
kind: 'error', | ||
title: t('reportScheduledErrorMsg', 'Failed to schedule a report'), | ||
description: t('reportScheduledErrorMsg', 'Failed to schedule a report'), | ||
}); | ||
closePanel(); | ||
setIsSubmitting(false); | ||
}, | ||
); | ||
}, | ||
[closePanel, renderModeUuid, reportRequestUuid, reportRequestUuid, reportParameters, schedule], | ||
); | ||
|
||
const handleOnChange = () => { | ||
setIgnoreChanges((prevState) => !prevState); | ||
}; | ||
|
||
const handleCronEditorChange = (cron: string, isValid: boolean) => { | ||
setSchedule(isValid ? cron : null); | ||
}; | ||
|
||
useEffect(() => { | ||
setIsSubmittable(!!schedule && !!renderModeUuid); | ||
}, [schedule, renderModeUuid]); | ||
|
||
return ( | ||
<Form className={styles.desktopEditSchedule} onChange={handleOnChange} onSubmit={handleSubmit}> | ||
<Stack gap={8} className={styles.container}> | ||
<SimpleCronEditor initialCron={initialCron} onChange={handleCronEditorChange} /> | ||
{reportDefinition && | ||
reportDefinition.parameters.map((parameter) => ( | ||
<ReportParameterInput | ||
parameter={parameter} | ||
value={reportRequest?.parameterMappings[parameter.name]} | ||
onChange={(parameterValue) => { | ||
setReportParameters((state) => ({ | ||
...state, | ||
[parameter.name]: parameterValue, | ||
})); | ||
}} | ||
/> | ||
))} | ||
<div className={styles.outputFormatDiv}> | ||
<Select | ||
className={styles.basicInputElement} | ||
labelText={t('outputFormat', 'Output format')} | ||
onChange={(e) => setRenderModeUuid(e.target.value)} | ||
value={renderModeUuid} | ||
> | ||
<SelectItem value={null} /> | ||
{reportDesigns && | ||
reportDesigns.map((reportDesign) => ( | ||
<SelectItem key={reportDesign.uuid} text={reportDesign.name} value={reportDesign.uuid}> | ||
{reportDesign.name} | ||
</SelectItem> | ||
))} | ||
</Select> | ||
</div> | ||
</Stack> | ||
<div className={styles.buttonsDiv}> | ||
<ButtonSet className={isTablet ? styles.tablet : styles.desktop}> | ||
<Button className={styles.button} kind="secondary" onClick={closePanel}> | ||
{t('cancel', 'Cancel')} | ||
</Button> | ||
<Button className={styles.button} disabled={isSubmitting || !isSubmittable} kind="primary" type="submit"> | ||
{t('save', 'Save')} | ||
</Button> | ||
</ButtonSet> | ||
</div> | ||
</Form> | ||
); | ||
}; | ||
|
||
export default EditScheduledReportForm; |
60 changes: 60 additions & 0 deletions
60
...ages/esm-reports-app/src/components/edit-scheduled-report/edit-scheduled-report-form.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@use '@carbon/styles/scss/type'; | ||
@import '~@openmrs/esm-styleguide/src/vars'; | ||
|
||
.tablet { | ||
padding: spacing.$spacing-06 spacing.$spacing-05; | ||
background-color: $ui-02; | ||
} | ||
|
||
.desktop { | ||
padding: 0rem; | ||
} | ||
|
||
.button { | ||
height: 4rem; | ||
display: flex; | ||
align-content: flex-start; | ||
align-items: baseline; | ||
min-width: 50%; | ||
} | ||
|
||
.container { | ||
margin: spacing.$spacing-05 0rem; | ||
background-color: $ui-background; | ||
|
||
& section { | ||
margin: spacing.$spacing-02 spacing.$spacing-05 0; | ||
} | ||
} | ||
|
||
.desktopEditSchedule { | ||
background-color: $ui-background; | ||
display: flex; | ||
flex-direction: column; | ||
justify-content: space-between; | ||
} | ||
|
||
.outputFormatDiv { | ||
margin-bottom: 50px; | ||
display: flex; | ||
padding: 32px 16px 16px 16px; | ||
flex-direction: column; | ||
align-items: flex-start; | ||
gap: 16px; | ||
} | ||
|
||
.basicInputElement { | ||
width: 300px; | ||
height: 30px; | ||
margin-bottom: 30px; | ||
} | ||
|
||
.buttonsDiv { | ||
margin-top: 50px; | ||
} | ||
|
||
.reportButton { | ||
max-width: none !important; | ||
width: 350px !important; | ||
} |
28 changes: 28 additions & 0 deletions
28
packages/esm-reports-app/src/components/next-report-execution.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import React from 'react'; | ||
import * as cronjsParser from '@datasert/cronjs-parser'; | ||
import * as cronjsMatcher from '@datasert/cronjs-matcher'; | ||
import moment from 'moment'; | ||
|
||
interface NextReportExecutionProps { | ||
schedule: string; | ||
currentDate: Date; | ||
} | ||
|
||
const NextReportExecution: React.FC<NextReportExecutionProps> = ({ schedule, currentDate }) => { | ||
const nextReportExecutionDate = (() => { | ||
if (!schedule) { | ||
return ''; | ||
} | ||
|
||
const expression = cronjsParser.parse(schedule, { hasSeconds: true }); | ||
const nextExecutions = cronjsMatcher.getFutureMatches(expression, { | ||
startAt: currentDate.toISOString(), | ||
matchCount: 1, | ||
}); | ||
return nextExecutions.length == 1 ? moment.utc(nextExecutions[0].toString()).format('YYYY-MM-DD HH:mm') : ''; | ||
})(); | ||
|
||
return <span>{nextReportExecutionDate}</span>; | ||
}; | ||
|
||
export default NextReportExecution; |
40 changes: 40 additions & 0 deletions
40
packages/esm-reports-app/src/components/overlay.component.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import React from 'react'; | ||
import { Button, Header } from '@carbon/react'; | ||
import { ArrowLeft, Close } from '@carbon/react/icons'; | ||
import { useLayoutType } from '@openmrs/esm-framework'; | ||
import { closeOverlay, useOverlay } from '../hooks/useOverlay'; | ||
import styles from './overlay.scss'; | ||
|
||
const Overlay: React.FC = () => { | ||
const { header, component, isOverlayOpen } = useOverlay(); | ||
const layout = useLayoutType(); | ||
const overlayClass = layout !== 'tablet' ? styles.desktopOverlay : styles.tabletOverlay; | ||
return ( | ||
<> | ||
{isOverlayOpen && ( | ||
<div className={overlayClass}> | ||
{layout === 'tablet' && ( | ||
<Header onClick={() => closeOverlay()} aria-label="Tablet overlay" className={styles.tabletOverlayHeader}> | ||
<Button hasIconOnly> | ||
<ArrowLeft size={16} /> | ||
</Button> | ||
<div className={styles.headerContent}>{header}</div> | ||
</Header> | ||
)} | ||
|
||
{layout !== 'tablet' && ( | ||
<div className={styles.desktopHeader}> | ||
<div className={styles.headerContent}>{header}</div> | ||
<Button className={styles.closePanelButton} onClick={() => closeOverlay()} kind="ghost" hasIconOnly> | ||
<Close size={16} /> | ||
</Button> | ||
</div> | ||
)} | ||
{component} | ||
</div> | ||
)} | ||
</> | ||
); | ||
}; | ||
|
||
export default Overlay; |
Oops, something went wrong.