diff --git a/packages/esm-commons-lib/src/components/reports/home.component.scss b/packages/esm-commons-lib/src/components/reports/home.component.scss index 5f252fe23..428173186 100644 --- a/packages/esm-commons-lib/src/components/reports/home.component.scss +++ b/packages/esm-commons-lib/src/components/reports/home.component.scss @@ -1,143 +1,165 @@ .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; - } + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 50px; + text-align: center; +} + +.container { + padding: 2rem; +} + +.homeContainer { + padding: 1rem; + width: 85vw +} + +.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); + height: 100vh; + display: flex; + flex-direction: row; + justify-content: flex-start; +} + +.dataTableFullContainer { + margin-top: 2rem; + padding: 1rem; + max-height: calc(100vh - 200px); + overflow: auto; +} + +.dataTable { + width: 100%; + border-collapse: collapse; + table-layout: fixed; +} + +.tableHeader { + white-space: normal; + word-wrap: break-word; + overflow-wrap: break-word; + max-width: 100px; + text-align: center; +} + +.tableCell { + white-space: normal; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.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; +} \ No newline at end of file diff --git a/packages/esm-commons-lib/src/components/reports/reportfilters.tsx b/packages/esm-commons-lib/src/components/reports/report-filters.component.tsx similarity index 100% rename from packages/esm-commons-lib/src/components/reports/reportfilters.tsx rename to packages/esm-commons-lib/src/components/reports/report-filters.component.tsx diff --git a/packages/esm-commons-lib/src/components/reports/reports-home.component.tsx b/packages/esm-commons-lib/src/components/reports/reports-home.component.tsx index 9ddc878b1..b959a6a34 100644 --- a/packages/esm-commons-lib/src/components/reports/reports-home.component.tsx +++ b/packages/esm-commons-lib/src/components/reports/reports-home.component.tsx @@ -1,5 +1,4 @@ -import React, { useState, useMemo, useEffect } from 'react'; -import useSWR from 'swr'; +import React, { useState, useMemo } from 'react'; import { DataTable, Table, @@ -13,20 +12,12 @@ import { DataTableSkeleton, } from '@carbon/react'; import { OHRIWelcomeSection } from '@ohri/openmrs-esm-ohri-commons-lib'; -import { openmrsFetch, useConfig, restBaseUrl } from '@openmrs/esm-framework'; +import { useConfig } 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 ReportFilters from './report-filters.component'; import { useTranslation } from 'react-i18next'; - -const snakeCaseToCapitalizedWords = (snakeCaseString) => - snakeCaseString - .split('_') - .map((word) => capitalize(word)) - .join(' '); - -const fetcher = (url) => openmrsFetch(url).then((res) => res.json()); +import { useReportsData } from './reports.resource'; const ReportComponent = () => { const config = useConfig(); @@ -38,34 +29,41 @@ const ReportComponent = () => { 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 { data, error, mutate } = useReportsData(startDate, endDate, reportId); 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, + if ( + !data || + !data.dataSets || + !data.dataSets.length || + !data.dataSets[0].metadata || + !data.dataSets[0].metadata.columns + ) + return []; + return data.dataSets[0].metadata.columns.map((col) => ({ + key: col.name.trim(), // Ensure key is a string and trimmed + header: col.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, - })); + if ( + !data || + !data.dataSets || + !data.dataSets.length || + !data.dataSets[0].rows || + !data.dataSets[0].metadata || + !data.dataSets[0].metadata.columns + ) + return []; + return data.dataSets[0].rows.map((row, idx) => { + const rowData = {}; + data.dataSets[0].metadata.columns.forEach((col) => { + const key = col.name.trim(); // Ensure key is a string and trimmed + rowData[key] = row[key] !== undefined ? row[key] : '-'; + }); + return { id: idx.toString(), ...rowData }; + }); }, [data]); const loading = !data && !error && reportRequested; @@ -106,7 +104,7 @@ const ReportComponent = () => { - ) : rows.length === 0 || !reportRequested ? ( + ) : rows?.length === 0 || !reportRequested ? (
@@ -122,11 +120,11 @@ const ReportComponent = () => {
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => ( - +
{headers.map((header) => ( - + {header.header} ))} @@ -134,9 +132,11 @@ const ReportComponent = () => { {rows.map((row) => ( - - {headers.map((header) => ( - {row[header.key] || '-'} + + {row.cells.map((cell) => ( + + {cell.value} + ))} ))} diff --git a/packages/esm-commons-lib/src/components/reports/reports.resource.tsx b/packages/esm-commons-lib/src/components/reports/reports.resource.tsx new file mode 100644 index 000000000..b3a5a47af --- /dev/null +++ b/packages/esm-commons-lib/src/components/reports/reports.resource.tsx @@ -0,0 +1,12 @@ +import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; +import useSWR from 'swr'; + +const fetcher = (url) => openmrsFetch(url).then((res) => res.json()); + +export function useReportsData(startDate: string, endDate: string, reportId: string) { + const url = `${restBaseUrl}/reportingrest/reportdata/${reportId}?startDate=${startDate}&endDate=${endDate}`; + + const { data, error, mutate, isLoading } = useSWR(url, fetcher, { revalidateOnFocus: false }); + + return { data, error, mutate, isLoading }; +}