diff --git a/packages/esm-billing-app/src/claims/claims-management/header/summary-header.component.tsx b/packages/esm-billing-app/src/claims/claims-management/header/summary-header.component.tsx new file mode 100644 index 000000000..70f693aa8 --- /dev/null +++ b/packages/esm-billing-app/src/claims/claims-management/header/summary-header.component.tsx @@ -0,0 +1,65 @@ +import { DatePicker, DatePickerInput } from '@carbon/react'; +import React, { useState, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from './summary-header.scss'; + +interface ClaimsSummaryHeaderProps { + filters: { + fromDate: Date | null; + toDate: Date | null; + }; + onFilterChanged: (updateFn: (currentFilters: any) => any) => void; + statusOptions?: string[]; +} + +const ClaimSummaryHeader: React.FC = ({ filters, onFilterChanged }) => { + const { t } = useTranslation(); + + const today = useMemo(() => new Date(), []); + const sixMonthsAgo = useMemo(() => { + const date = new Date(today); + date.setMonth(today.getMonth() - 6); + return date; + }, [today]); + + useEffect(() => { + if (!filters.fromDate && !filters.toDate) { + onFilterChanged(() => ({ + fromDate: sixMonthsAgo, + toDate: today, + })); + } + }, [filters, onFilterChanged, sixMonthsAgo, today]); + + const handleDateChange = ([fromDate, toDate]: [Date, Date]) => { + onFilterChanged(() => ({ + fromDate, + toDate, + })); + }; + + return ( +
+ + + + +
+ ); +}; + +export default ClaimSummaryHeader; diff --git a/packages/esm-billing-app/src/claims/claims-management/header/summary-header.scss b/packages/esm-billing-app/src/claims/claims-management/header/summary-header.scss new file mode 100644 index 000000000..db493f540 --- /dev/null +++ b/packages/esm-billing-app/src/claims/claims-management/header/summary-header.scss @@ -0,0 +1,13 @@ +@use '@carbon/layout'; +@use '@carbon/type'; +@use '@carbon/colors'; + +.summaryContainer { + display: flex; + margin-top: layout.$spacing-05; + gap: layout.$spacing-01; +} + +.input { + min-width: 300px; +} diff --git a/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-chart.component.tsx b/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-chart.component.tsx new file mode 100644 index 000000000..fe5aa557a --- /dev/null +++ b/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-chart.component.tsx @@ -0,0 +1,60 @@ +import React, { useEffect, useState } from 'react'; +import { BarChartOptions, GroupedBarChart, ScaleTypes } from '@carbon/charts-react'; +import useClaimsAggregate from '../../../hooks/useClaimsAggregate'; + +interface MetricData { + summaryGraph: { group: string; month: string; value: number }[]; +} + +const ClaimsSummaryChart = () => { + const [metrics, setMetrics] = useState({ summaryGraph: [] }); + + const { isLoading, summarizedData, error } = useClaimsAggregate(); + + useEffect(() => { + if (summarizedData?.length) { + const transformedData = summarizedData.flatMap((item) => [ + { group: 'Claimed', month: item.month, value: item.claimedTotal }, + { group: 'Approved', month: item.month, value: item.approvedTotal }, + ]); + setMetrics({ summaryGraph: transformedData }); + } + }, [summarizedData]); + + if (isLoading) { + return
Loading data...
; + } + + if (error) { + return
Error loading claims data: {error.message}
; + } + + const options: BarChartOptions = { + title: 'Analysis of Claimed vs Approved Amount by Month', + legend: { + enabled: true, + }, + axes: { + left: { + mapsTo: 'month', + title: 'Month', + scaleType: ScaleTypes.LABELS, + }, + bottom: { + mapsTo: 'value', + title: 'Amount (Ksh)', + scaleType: ScaleTypes.LINEAR, + includeZero: true, + }, + }, + height: '400px', + }; + + return ( +
+ +
+ ); +}; + +export default ClaimsSummaryChart; diff --git a/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-main.component.tsx b/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-main.component.tsx new file mode 100644 index 000000000..8b2d789c1 --- /dev/null +++ b/packages/esm-billing-app/src/claims/claims-management/main/claims-summary-main.component.tsx @@ -0,0 +1,79 @@ +import React, { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; +import styles from '../../metrics/metrics.scss'; +import { ClaimsManagementHeader } from '../header/claims-header.component'; +import ClaimsSummaryHeader from '../header/summary-header.component'; +import { ClaimsSummaryFilter } from '../../../types'; +import ClaimsSummaryChart from './claims-summary-chart.component'; + +import MetricsCard from '../../metrics/metrics-card.component'; +import { convertToCurrency } from '../../../helpers'; +import useClaimsAggregate from '../../../hooks/useClaimsAggregate'; + +const MainMetrics = () => { + const [filters, setFilters] = useState({ + fromDate: null, + toDate: null, + }); + const onFilterChanged = (updateFn: (currentFilters: any) => any) => { + setFilters(updateFn(filters)); + }; + + const t = (key, fallback) => fallback; + + const { isLoading, summarizedData, error } = useClaimsAggregate(); + + if (error) { + return
Error loading claims data
; + } + + if (isLoading) { + return
Loading claims data...
; + } + + const totalClaimed = summarizedData.reduce((sum, item) => sum + item.claimedTotal, 0); + const totalApproved = summarizedData.reduce((sum, item) => sum + item.approvedTotal, 0); + const totalPending = totalClaimed - totalApproved; + + const preApps = 0; + const preAppsApproved = 0; + const preAppsPending = 0; + + return ( +
+ + + <> +
+ + + +
+
+ + + +
+ + +
+ ); +}; + +export default MainMetrics; diff --git a/packages/esm-billing-app/src/hooks/useClaimsAggregate.ts b/packages/esm-billing-app/src/hooks/useClaimsAggregate.ts new file mode 100644 index 000000000..99991b596 --- /dev/null +++ b/packages/esm-billing-app/src/hooks/useClaimsAggregate.ts @@ -0,0 +1,66 @@ +import { useMemo } from 'react'; +import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; +import useSWR from 'swr'; + +interface Claim { + uuid: string; + claimCode: string; + dateFrom: string | null; + dateTo: string | null; + claimedTotal: number; + approvedTotal: number; + status: string; + externalId?: string; +} + +interface ClaimResponse { + results: Claim[]; +} + +const useClaimsAggregate = () => { + const { data, error, isValidating } = useSWR( + `${restBaseUrl}/claim?v=custom:(uuid,claimCode,dateFrom,dateTo,claimedTotal,approvedTotal,status,externalId)`, + async (url) => { + const response = await openmrsFetch(url); + return response.data; + }, + ); + + const summarizedData = useMemo(() => { + if (!data || !data.results) { + return []; + } + + const summary = data.results.reduce((acc, item) => { + const date = item.dateFrom || item.dateTo; + const month = new Date(date ?? '').toLocaleString('default', { + month: 'long', + year: 'numeric', + }); + + if (!acc[month]) { + acc[month] = { claimedTotal: 0, approvedTotal: 0 }; + } + + acc[month].claimedTotal += item.claimedTotal || 0; + acc[month].approvedTotal += item.approvedTotal || 0; + + return acc; + }, {} as Record); + + return Object.entries(summary).map(([month, totals], index) => ({ + id: `${index + 1}`, + month, + claimedTotal: totals.claimedTotal, + approvedTotal: totals.approvedTotal, + })); + }, [data]); + + return { + isLoading: isValidating, + error, + summarizedData, + }; +}; + +export default useClaimsAggregate; diff --git a/packages/esm-billing-app/src/index.ts b/packages/esm-billing-app/src/index.ts index cad93dc0c..f9e47f395 100644 --- a/packages/esm-billing-app/src/index.ts +++ b/packages/esm-billing-app/src/index.ts @@ -128,6 +128,13 @@ export const claimsManagementSideNavGroup = getSyncLifecycle( }), options, ); +export const claimsSummaryOverviewDashboardLink = getSyncLifecycle( + createLeftPanelLink({ + name: 'claims-summary', + title: 'Claims Summary', + }), + options, +); export const claimsManagementOverviewDashboardLink = getSyncLifecycle( createLeftPanelLink({ name: 'claims-overview', diff --git a/packages/esm-billing-app/src/root.component.tsx b/packages/esm-billing-app/src/root.component.tsx index 4dc815072..3173852b0 100644 --- a/packages/esm-billing-app/src/root.component.tsx +++ b/packages/esm-billing-app/src/root.component.tsx @@ -5,6 +5,7 @@ import BillManager from './billable-services/bill-manager/bill-manager.component import { ChargeItemsDashboard } from './billable-services/dashboard/dashboard.component'; import { PaymentHistory } from './billable-services/payment-history/payment-history.component'; import { BillingDashboard } from './billing-dashboard/billing-dashboard.component'; +import ClaimsManagementSummary from './claims/claims-management/main/claims-summary-main.component'; import ClaimsManagementOverview from './claims/claims-management/main/claims-overview-main.component'; import ClaimsManagementPreAuthRequest from './claims/claims-management/main/claims-pre-auth-main.component'; import ClaimScreen from './claims/dashboard/claims-dashboard.component'; @@ -21,6 +22,7 @@ const RootComponent: React.FC = () => { } /> + } /> } /> } />