-
Notifications
You must be signed in to change notification settings - Fork 40
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6807f93
commit 1045d03
Showing
5 changed files
with
382 additions
and
9 deletions.
There are no files selected for viewing
143 changes: 143 additions & 0 deletions
143
packages/esm-commons-lib/src/components/reports/home.component.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,143 @@ | ||
.centeredTextContainer { | ||
display: flex; | ||
flex-direction: column; | ||
align-items: center; | ||
justify-content: center; | ||
height: 50px; | ||
text-align: center; | ||
} | ||
|
||
.container { | ||
padding: 2rem; | ||
} | ||
|
||
.homeContainer { | ||
padding: 1rem; | ||
} | ||
|
||
.dropdownItem { | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.layer { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 300px; | ||
} | ||
|
||
.tile { | ||
padding: 2rem; | ||
text-align: center; | ||
} | ||
|
||
.content { | ||
font-size: 1.25rem; | ||
color: #5a5a5a; | ||
} | ||
|
||
.explainer { | ||
color: #777; | ||
} | ||
|
||
.form { | ||
display: flex; | ||
flex-direction: column; | ||
gap: 1rem; | ||
} | ||
|
||
.formContainer { | ||
display: flex; | ||
flex-direction: column; | ||
} | ||
|
||
.datePickerContainer { | ||
display: flex; | ||
align-items: center; | ||
gap: 8px; | ||
justify-content: space-between; | ||
flex-wrap: wrap; | ||
} | ||
|
||
.datePickerContainer > * { | ||
flex: 1; | ||
min-width: 150px; | ||
} | ||
|
||
.fetchButtonContainer { | ||
margin-left: 16px; | ||
display: flex; | ||
align-items: center; | ||
} | ||
|
||
.datePickerInput { | ||
min-width: 120px; | ||
} | ||
|
||
.button { | ||
max-height: 40px; | ||
line-height: 40px; | ||
font-size: 14px; | ||
padding: 0 16px; | ||
margin-top: 1rem; | ||
max-width: 120px; | ||
align-items: center; | ||
} | ||
|
||
.dataTableContainer { | ||
margin-top: 2rem; | ||
padding: 1rem; | ||
border: solid 1px #e0e0e0; | ||
max-height: calc(100vh - 200px); | ||
overflow: auto; | ||
height: 100vh; | ||
} | ||
|
||
.dataTableFullContainer { | ||
margin-top: 2rem; | ||
padding: 1rem; | ||
max-height: calc(100vh - 200px); | ||
overflow: auto; | ||
} | ||
|
||
|
||
.tableContainer { | ||
margin-top: 1rem; | ||
} | ||
|
||
.toolbarWrapper { | ||
display: flex; | ||
justify-content: space-between; | ||
align-items: center; | ||
} | ||
|
||
.toolbarContent { | ||
display: flex; | ||
gap: 1rem; | ||
} | ||
|
||
.searchbox { | ||
flex-grow: 1; | ||
} | ||
|
||
.tileContainer { | ||
display: flex; | ||
justify-content: center; | ||
align-items: center; | ||
height: 200px; | ||
} | ||
|
||
.tileContent { | ||
text-align: center; | ||
} | ||
|
||
.content { | ||
font-size: 1.25rem; | ||
color: #5a5a5a; | ||
} | ||
|
||
.pagination { | ||
margin-top: 1rem; | ||
} | ||
|
75 changes: 75 additions & 0 deletions
75
packages/esm-commons-lib/src/components/reports/reportfilters.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,75 @@ | ||
import React from 'react'; | ||
import { ComboBox, Button, DatePicker, DatePickerInput } from '@carbon/react'; | ||
import styles from './home.component.scss'; | ||
|
||
const ReportFilters = ({ | ||
config, | ||
uuid, | ||
reportId, | ||
setReportId, | ||
ptrackerId, | ||
setPtrackerId, | ||
personUuid, | ||
setPersonUuid, | ||
startDate, | ||
setStartDate, | ||
endDate, | ||
setEndDate, | ||
handleSubmit, | ||
setReportRequested, | ||
}) => { | ||
const handleDateChange = (setter) => (event) => { | ||
const date = event[0] ? event[0].toISOString().split('T')[0] : ''; | ||
setter(date); | ||
}; | ||
|
||
return ( | ||
<> | ||
<div className={styles.centeredTextContainer}> | ||
<h2>Report Filters</h2> | ||
</div> | ||
<form className={styles.form} onSubmit={handleSubmit}> | ||
<div className={styles.datePickerContainer}> | ||
<ComboBox | ||
id="report-dropdown" | ||
titleText="Select Report" | ||
label="Select a report to display" | ||
items={config.reports} | ||
itemToString={(item) => (item ? item.name : '')} | ||
onChange={({ selectedItem }) => { | ||
if (selectedItem) { | ||
setReportId(selectedItem.uuid || ''); | ||
} else { | ||
setReportId(''); | ||
setPtrackerId(''); | ||
setPersonUuid(''); | ||
setStartDate(''); | ||
setEndDate(''); | ||
setReportRequested(false); | ||
} | ||
}} | ||
/> | ||
<DatePicker | ||
datePickerType="single" | ||
onChange={handleDateChange(setStartDate)} | ||
value={startDate ? [new Date(startDate)] : []} | ||
> | ||
<DatePickerInput id="start-date" labelText="Start Date" placeholder="yyyy-mm-dd" /> | ||
</DatePicker> | ||
<DatePicker | ||
datePickerType="single" | ||
onChange={handleDateChange(setEndDate)} | ||
value={endDate ? [new Date(endDate)] : []} | ||
> | ||
<DatePickerInput id="end-date" labelText="End Date" placeholder="yyyy-mm-dd" /> | ||
</DatePicker> | ||
<Button className={styles.button} kind="tertiary" type="submit"> | ||
View Report | ||
</Button> | ||
</div> | ||
</form> | ||
</> | ||
); | ||
}; | ||
|
||
export default ReportFilters; |
153 changes: 146 additions & 7 deletions
153
packages/esm-commons-lib/src/components/reports/reports-home.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 |
---|---|---|
@@ -1,14 +1,153 @@ | ||
import React from 'react'; | ||
import React, { useState, useMemo, useEffect } from 'react'; | ||
import useSWR from 'swr'; | ||
import { | ||
DataTable, | ||
Table, | ||
TableHead, | ||
TableRow, | ||
TableHeader, | ||
TableBody, | ||
TableCell, | ||
Layer, | ||
Tile, | ||
DataTableSkeleton, | ||
} from '@carbon/react'; | ||
import { OHRIWelcomeSection } from '@ohri/openmrs-esm-ohri-commons-lib'; | ||
import { Reports } from './reports'; | ||
import { openmrsFetch, useConfig, restBaseUrl } from '@openmrs/esm-framework'; | ||
import styles from './home.component.scss'; | ||
import capitalize from 'lodash/capitalize'; | ||
import { EmptyDataIllustration } from '@openmrs/esm-patient-common-lib'; | ||
import ReportFilters from './reportfilters'; | ||
import { useTranslation } from 'react-i18next'; | ||
|
||
const snakeCaseToCapitalizedWords = (snakeCaseString) => | ||
snakeCaseString | ||
.split('_') | ||
.map((word) => capitalize(word)) | ||
.join(' '); | ||
|
||
const fetcher = (url) => openmrsFetch(url).then((res) => res.json()); | ||
|
||
const ReportComponent = () => { | ||
const config = useConfig(); | ||
const { t } = useTranslation(); | ||
const [reportId, setReportId] = useState(''); | ||
const [uuid, setUuid] = useState(''); | ||
const [ptrackerId, setPtrackerId] = useState(''); | ||
const [personUuid, setPersonUuid] = useState(''); | ||
const [startDate, setStartDate] = useState(''); | ||
const [endDate, setEndDate] = useState(''); | ||
const [reportRequested, setReportRequested] = useState(false); | ||
|
||
const url = useMemo(() => { | ||
if (!startDate || !endDate || !reportId) return null; | ||
return `${restBaseUrl}/reportingrest/reportdata/${reportId}?startDate=${startDate}&endDate=${endDate}`; | ||
}, [reportId, startDate, endDate]); | ||
|
||
const { data, error, mutate } = useSWR(url, fetcher, { revalidateOnFocus: false }); | ||
|
||
useEffect(() => { | ||
if (error) { | ||
console.error('Error fetching report data:', error); | ||
} | ||
}, [error]); | ||
|
||
const headers = useMemo(() => { | ||
if (!data || !data.dataSets || !data.dataSets.length || !data.dataSets[0].metadata) return []; | ||
return data.dataSets[0].metadata.columns.map((column) => ({ | ||
key: column.name, | ||
header: column.label, | ||
})); | ||
}, [data]); | ||
|
||
const rows = useMemo(() => { | ||
if (!data || !data.dataSets || !data.dataSets.length || !data.dataSets[0].rows) return []; | ||
return data.dataSets[0].rows.map((result, idx) => ({ | ||
id: idx.toString(), | ||
...result, | ||
})); | ||
}, [data]); | ||
|
||
const loading = !data && !error && reportRequested; | ||
|
||
const handleSubmit = (e) => { | ||
e.preventDefault(); | ||
setReportRequested(true); | ||
mutate(); | ||
}; | ||
|
||
const ReportsHomecomponent = () => { | ||
return ( | ||
<div> | ||
<OHRIWelcomeSection title="Reports" /> | ||
<Reports /> | ||
<div className={styles.homeContainer}> | ||
<OHRIWelcomeSection title={t('reportingDemo', 'Reporting demo')} /> | ||
<ReportFilters | ||
config={config} | ||
reportId={reportId} | ||
setReportId={setReportId} | ||
ptrackerId={ptrackerId} | ||
setPtrackerId={setPtrackerId} | ||
personUuid={personUuid} | ||
setPersonUuid={setPersonUuid} | ||
startDate={startDate} | ||
setStartDate={setStartDate} | ||
endDate={endDate} | ||
setEndDate={setEndDate} | ||
handleSubmit={handleSubmit} | ||
setReportRequested={setReportRequested} | ||
uuid={uuid} | ||
/> | ||
{loading ? ( | ||
<DataTableSkeleton columnCount={headers.length} rowCount={rows.length} /> | ||
) : error ? ( | ||
<div className={styles.dataTableContainer}> | ||
<Layer className={styles.layer}> | ||
<Tile className={styles.tile}> | ||
<p className={styles.content}>{t('errorLoadingData', 'Error loading data')}</p> | ||
<p className={styles.explainer}>{t('pleaseTryAgain', 'Please try again later')}</p> | ||
</Tile> | ||
</Layer> | ||
</div> | ||
) : rows.length === 0 || !reportRequested ? ( | ||
<div className={styles.dataTableContainer}> | ||
<Layer className={styles.layer}> | ||
<Tile className={styles.tile}> | ||
<EmptyDataIllustration /> | ||
<p className={styles.content}>{t('noDataToDisplay', 'No data to display')}</p> | ||
<p className={styles.explainer}> | ||
{t('useReportsAboveToBuild', 'Use the report filters above to build your reports')} | ||
</p> | ||
</Tile> | ||
</Layer> | ||
</div> | ||
) : ( | ||
<div className={styles.dataTableFullContainer}> | ||
<DataTable rows={rows} headers={headers}> | ||
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => ( | ||
<Table {...getTableProps()}> | ||
<TableHead> | ||
<TableRow> | ||
{headers.map((header) => ( | ||
<TableHeader {...getHeaderProps({ header })} key={header.key}> | ||
{header.header} | ||
</TableHeader> | ||
))} | ||
</TableRow> | ||
</TableHead> | ||
<TableBody> | ||
{rows.map((row) => ( | ||
<TableRow {...getRowProps({ row })} key={row.id}> | ||
{headers.map((header) => ( | ||
<TableCell key={header.key}>{row[header.key] || '-'}</TableCell> | ||
))} | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
)} | ||
</DataTable> | ||
</div> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default ReportsHomecomponent; | ||
export default ReportComponent; |
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
Oops, something went wrong.