@kenyaemr/esm-billing-app 5.4.1-pre.2092 → 5.4.1-pre.2096

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.
@@ -1,208 +1,266 @@
1
- import {
2
- DataTable,
3
- DataTableSkeleton,
4
- OverflowMenu,
5
- OverflowMenuItem,
6
- Pagination,
7
- Table,
8
- TableBody,
9
- TableCell,
10
- TableContainer,
11
- TableHead,
12
- TableHeader,
13
- TableRow,
14
- Tag,
15
- } from '@carbon/react';
16
- import { formatDate, isDesktop, parseDate, showModal, useLayoutType, usePagination } from '@openmrs/esm-framework';
17
- import { CardHeader, EmptyState, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
18
- import React, { useMemo, useState } from 'react';
19
- import { useTranslation } from 'react-i18next';
20
- import { ClaimsPreAuthFilter } from '../../../types';
21
- import ClaimsFilterHeader from '../header/filter-header.component';
22
- import styles from './claims-list-table.scss';
23
- import { useFacilityClaims } from './use-facility-claims';
1
+ // import {
2
+ // DataTable,
3
+ // DataTableSkeleton,
4
+ // OverflowMenu,
5
+ // OverflowMenuItem,
6
+ // Pagination,
7
+ // Table,
8
+ // TableBody,
9
+ // TableCell,
10
+ // TableContainer,
11
+ // TableHead,
12
+ // TableHeader,
13
+ // TableRow,
14
+ // Tag,
15
+ // } from '@carbon/react';
16
+ // import { formatDate, isDesktop, parseDate, showModal, useLayoutType, usePagination } from '@openmrs/esm-framework';
17
+ // import { CardHeader, EmptyState, usePaginationInfo } from '@openmrs/esm-patient-common-lib';
18
+ // import React, { useMemo, useState } from 'react';
19
+ // import { useTranslation } from 'react-i18next';
20
+ // import { ClaimsPreAuthFilter } from '../../../types';
21
+ // import ClaimsFilterHeader from '../header/filter-header.component';
22
+ // import styles from './claims-list-table.scss';
23
+ // import { useFacilityClaims } from './use-facility-claims';
24
+ // import { statusColors } from '../../utils';
24
25
 
25
- const ClaimsManagementTable: React.FC = () => {
26
- const { claims, isLoading } = useFacilityClaims();
27
- const { t } = useTranslation();
28
- const [filters, setFilters] = useState<ClaimsPreAuthFilter>({
29
- status: 'all',
30
- });
31
- const status = useMemo(
32
- () => [
33
- { value: 'all', label: t('all', 'All') },
34
- { value: 'ENTERED', label: t('entered', 'Entered') },
35
- { value: 'ERRORED', label: t('errored', 'Errored') },
36
- { value: 'REJECTED', label: t('rejected', 'Rejected') },
37
- { value: 'CHECKED', label: t('checked', 'Checked') },
38
- { value: 'VALUATED', label: t('valuated', 'Valuated') },
39
- ],
40
- [t],
41
- );
42
- const [pageSize, setPageSize] = useState(5);
43
- const filterClaims = (claim) => {
44
- const status = filters?.status;
45
- const search = filters?.search?.toLowerCase();
46
- const fromDate = filters?.fromDate ? new Date(filters.fromDate) : null;
47
- const toDate = filters?.toDate ? new Date(filters.toDate) : null;
48
-
49
- const patientName = claim.patientName?.toLowerCase();
50
- const providerName = claim.providerName?.toLowerCase();
51
- const timeStamp = new Date(claim.dateFrom);
52
- const preauthStatus = claim.status;
53
-
54
- // Check status (allow 'all' to bypass the filter)
55
- const statusMatch = status === 'all' || preauthStatus === status;
56
-
57
- // Check search filter (patient name or provider name contains the search term)
58
- const searchMatch = !search || patientName?.includes(search) || providerName?.includes(search);
59
-
60
- // Check date range filter (timestamp is between fromDate and toDate if provided)
61
- const dateMatch = (!fromDate || timeStamp >= fromDate) && (!toDate || timeStamp <= toDate);
62
-
63
- // Return true if all conditions match
64
- return statusMatch && searchMatch && dateMatch;
65
- };
66
-
67
- const filteredClaims = claims.filter(filterClaims);
68
- const { paginated, goTo, results, currentPage } = usePagination(filteredClaims, pageSize);
69
- const { pageSizes } = usePaginationInfo(pageSize, claims.length, currentPage, results.length);
70
- const responsiveSize = isDesktop(useLayoutType()) ? 'sm' : 'lg';
71
- const layout = useLayoutType();
72
- const size = layout === 'tablet' ? 'lg' : 'md';
73
-
74
- const headers = [
75
- { key: 'claimCode', header: t('claimCode', 'Claim Code') },
76
- { key: 'status', header: t('status', 'Status') },
77
- { key: 'providerName', header: t('provider', 'Provider') },
78
- { key: 'patientName', header: t('patient', 'Patient') },
79
- { key: 'claimedTotal', header: t('claimedAmount', 'Claimed Amount') },
80
- { key: 'approvedTotal', header: t('approvedTotal', 'Approved Total') },
81
- { key: 'dateFrom', header: t('dateCreated', 'Date Created') },
82
- { key: 'action', header: t('action', 'Action') },
83
- ];
84
-
85
- if (isLoading) {
86
- return (
87
- <div className={styles.dataTableSkeleton}>
88
- <DataTableSkeleton
89
- headers={headers}
90
- showToolbar={false}
91
- showHeader={false}
92
- columnCount={Object.keys(headers).length}
93
- zebra
94
- rowCount={pageSize}
95
- />
96
- </div>
97
- );
98
- }
99
-
100
- if (claims.length === 0) {
101
- return (
102
- <div className={styles.claimsTable}>
103
- <EmptyState
104
- displayText={t('emptyClaimsState', 'There are no claims to display')}
105
- headerTitle={t('emptyClaimsHeader', 'No Claims')}
106
- />
107
- </div>
108
- );
109
- }
110
-
111
- const handleRetry = (claimId: string) => {
112
- const dispose = showModal('retry-claim-request-modal', {
113
- closeModal: () => dispose(),
114
- claimId,
115
- });
116
- };
26
+ // const ClaimsManagementTable: React.FC = () => {
27
+ // const { claims, isLoading } = useFacilityClaims();
28
+ // const { t } = useTranslation();
29
+ // const [filters, setFilters] = useState<ClaimsPreAuthFilter>({
30
+ // status: 'all',
31
+ // });
32
+ // const status = useMemo(
33
+ // () => [
34
+ // { value: 'all', label: t('all', 'All') },
35
+ // { value: 'ENTERED', label: t('entered', 'Entered') },
36
+ // { value: 'ERRORED', label: t('errored', 'Errored') },
37
+ // { value: 'REJECTED', label: t('rejected', 'Rejected') },
38
+ // { value: 'CHECKED', label: t('checked', 'Checked') },
39
+ // { value: 'VALUATED', label: t('valuated', 'Valuated') },
40
+ // { value: 'APPROVED', label: t('approved', 'Approved') },
41
+ // ],
42
+ // [t],
43
+ // );
44
+ // const [pageSize, setPageSize] = useState(5);
45
+ // const filterClaims = (claim) => {
46
+ // const status = filters?.status;
47
+ // const search = filters?.search?.toLowerCase();
48
+ // const fromDate = filters?.fromDate ? new Date(filters.fromDate) : null;
49
+ // const toDate = filters?.toDate ? new Date(filters.toDate) : null;
117
50
 
118
- return (
119
- <div className={styles.dataTableSkeleton}>
120
- <div className={styles.tableHeader}>
121
- <CardHeader title={t('claims', 'Claims')}>{''}</CardHeader>
122
- <ClaimsFilterHeader filters={filters} onFilterChanged={setFilters} statusOptions={status} />
123
- </div>
124
- <DataTable rows={results} headers={headers} isSortable useZebraStyles>
125
- {({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => (
126
- <TableContainer className={styles.claimsTable}>
127
- <Table {...getTableProps()} aria-label="sample table">
128
- <TableHead>
129
- <TableRow>
130
- {headers.map((header) => (
131
- <TableHeader
132
- key={header.key}
133
- {...getHeaderProps({
134
- header,
135
- })}>
136
- {header.header}
137
- </TableHeader>
138
- ))}
139
- </TableRow>
140
- </TableHead>
141
- <TableBody>
142
- {rows.map((row) => {
143
- const rowStatus = row.cells.find((cell) => cell.info.header === 'status')?.value;
144
- return (
145
- <TableRow
146
- {...getRowProps({
147
- row,
148
- })}>
149
- {row.cells.map((cell) => (
150
- <TableCell key={cell.id}>
151
- {cell.info.header === 'status' ? (
152
- <ClaimStatus cell={cell} row={row} />
153
- ) : cell.info.header === 'action' && ['ENTERED', 'ERRORED'].includes(rowStatus) ? (
154
- <OverflowMenu size={size} flipped>
155
- <OverflowMenuItem
156
- itemText={t('retryRequest', 'Retry request')}
157
- onClick={() => handleRetry(row.id)}
158
- />
159
- </OverflowMenu>
160
- ) : cell.info.header === 'dateFrom' ? (
161
- formatDate(parseDate(cell.value))
162
- ) : (
163
- cell.value
164
- )}
165
- </TableCell>
166
- ))}
167
- </TableRow>
168
- );
169
- })}
170
- </TableBody>
171
- </Table>
172
- {paginated && !isLoading && (
173
- <Pagination
174
- forwardText=""
175
- backwardText=""
176
- page={currentPage}
177
- pageSize={pageSize}
178
- pageSizes={pageSizes}
179
- totalItems={filteredClaims.length}
180
- size={responsiveSize}
181
- onChange={({ page: newPage, pageSize }) => {
182
- if (newPage !== currentPage) {
183
- goTo(newPage);
184
- }
185
- setPageSize(pageSize);
186
- }}
187
- />
188
- )}
189
- </TableContainer>
190
- )}
191
- </DataTable>
192
- </div>
193
- );
194
- };
51
+ // const patientName = claim.patientName?.toLowerCase();
52
+ // const providerName = claim.providerName?.toLowerCase();
53
+ // const timeStamp = new Date(claim.dateFrom);
54
+ // const preauthStatus = claim.status;
195
55
 
196
- export default ClaimsManagementTable;
56
+ // // Check status (allow 'all' to bypass the filter)
57
+ // const statusMatch = status === 'all' || preauthStatus === status;
58
+
59
+ // // Check search filter (patient name or provider name contains the search term)
60
+ // const searchMatch = !search || patientName?.includes(search) || providerName?.includes(search);
61
+
62
+ // // Check date range filter (timestamp is between fromDate and toDate if provided)
63
+ // const dateMatch = (!fromDate || timeStamp >= fromDate) && (!toDate || timeStamp <= toDate);
64
+
65
+ // // Return true if all conditions match
66
+ // return statusMatch && searchMatch && dateMatch;
67
+ // };
68
+
69
+ // const filteredClaims = claims.filter(filterClaims);
70
+ // const { paginated, goTo, results, currentPage } = usePagination(filteredClaims, pageSize);
71
+ // const { pageSizes } = usePaginationInfo(pageSize, claims.length, currentPage, results.length);
72
+ // const responsiveSize = isDesktop(useLayoutType()) ? 'sm' : 'lg';
73
+ // const layout = useLayoutType();
74
+ // const size = layout === 'tablet' ? 'lg' : 'md';
75
+
76
+ // const headers = [
77
+ // { key: 'claimCode', header: t('claimCode', 'Claim Code') },
78
+ // { key: 'status', header: t('status', 'Status') },
79
+ // { key: 'providerName', header: t('provider', 'Provider') },
80
+ // { key: 'patientName', header: t('patient', 'Patient') },
81
+ // { key: 'claimedTotal', header: t('claimedAmount', 'Claimed Amount') },
82
+ // { key: 'approvedTotal', header: t('approvedTotal', 'Approved Total') },
83
+ // { key: 'dateFrom', header: t('dateCreated', 'Date Created') },
84
+ // { key: 'action', header: t('action', 'Action') },
85
+ // ];
86
+
87
+ // if (isLoading) {
88
+ // return (
89
+ // <div className={styles.dataTableSkeleton}>
90
+ // <DataTableSkeleton
91
+ // headers={headers}
92
+ // showToolbar={false}
93
+ // showHeader={false}
94
+ // columnCount={Object.keys(headers).length}
95
+ // zebra
96
+ // rowCount={pageSize}
97
+ // />
98
+ // </div>
99
+ // );
100
+ // }
101
+
102
+ // if (claims.length === 0) {
103
+ // return (
104
+ // <div className={styles.claimsTable}>
105
+ // <EmptyState
106
+ // displayText={t('emptyClaimsState', 'There are no claims to display')}
107
+ // headerTitle={t('emptyClaimsHeader', 'No Claims')}
108
+ // />
109
+ // </div>
110
+ // );
111
+ // }
112
+
113
+ // const handleClaimAction = (claimId: string, modalType: 'retry' | 'update') => {
114
+ // const dispose = showModal('manage-claim-request-modal', {
115
+ // closeModal: () => dispose(),
116
+ // claimId,
117
+ // modalType,
118
+ // });
119
+ // };
120
+
121
+ // const renderStatusCell = (row) => {
122
+ // return <ClaimStatus row={row} />;
123
+ // };
124
+
125
+ // const renderActionCell = (row, rowStatus, size) => {
126
+ // return (
127
+ // <OverflowMenu size={size} flipped>
128
+ // {['ENTERED', 'ERRORED'].includes(rowStatus) && (
129
+ // <OverflowMenuItem
130
+ // itemText={t('retryRequest', 'Retry request')}
131
+ // onClick={() => handleClaimAction(row.id, 'retry')}
132
+ // />
133
+ // )}
134
+ // <OverflowMenuItem
135
+ // itemText={t('updateStatus', 'Update status')}
136
+ // onClick={() => handleClaimAction(row.id, 'update')}
137
+ // />
138
+ // </OverflowMenu>
139
+ // );
140
+ // };
197
141
 
198
- const ClaimStatus = ({ cell, row }: { cell: { value: string }; row: any }) => {
199
- const { claims } = useFacilityClaims();
142
+ // const renderDateFromCell = (cellValue) => {
143
+ // return formatDate(parseDate(cellValue));
144
+ // };
200
145
 
201
- const claim = claims.find((claim) => claim.id === row.id);
146
+ // const renderApprovedTotalCell = (row, rowStatus, cellValue) => {
147
+ // if (['APPROVED'].includes(rowStatus)) {
148
+ // const claimedTotal = row.cells.find((c) => c.info.header === 'claimedTotal')?.value;
149
+ // return claimedTotal || cellValue;
150
+ // }
151
+ // return cellValue;
152
+ // };
153
+
154
+ // const renderCellContent = (cell, row, rowStatus) => {
155
+ // const header = cell.info.header;
156
+
157
+ // if (header === 'status') {
158
+ // return renderStatusCell(row);
159
+ // }
160
+
161
+ // if (header === 'action') {
162
+ // return renderActionCell(row, rowStatus, size);
163
+ // }
164
+
165
+ // if (header === 'dateFrom') {
166
+ // return renderDateFromCell(cell.value);
167
+ // }
168
+
169
+ // if (header === 'approvedTotal') {
170
+ // return renderApprovedTotalCell(row, rowStatus, cell.value);
171
+ // }
172
+
173
+ // return cell.value;
174
+ // };
175
+
176
+ // return (
177
+ // <div className={styles.dataTableSkeleton}>
178
+ // <div className={styles.tableHeader}>
179
+ // <CardHeader title={t('claims', 'Claims')}>{''}</CardHeader>
180
+ // <ClaimsFilterHeader filters={filters} onFilterChanged={setFilters} statusOptions={status} />
181
+ // </div>
182
+ // <DataTable rows={results} headers={headers} isSortable useZebraStyles>
183
+ // {({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => (
184
+ // <TableContainer className={styles.claimsTable}>
185
+ // <Table {...getTableProps()} aria-label="sample table">
186
+ // <TableHead>
187
+ // <TableRow>
188
+ // {headers.map((header) => (
189
+ // <TableHeader
190
+ // key={header.key}
191
+ // {...getHeaderProps({
192
+ // header,
193
+ // })}>
194
+ // {header.header}
195
+ // </TableHeader>
196
+ // ))}
197
+ // </TableRow>
198
+ // </TableHead>
199
+ // <TableBody>
200
+ // {rows.map((row) => {
201
+ // const rowStatus = row.cells.find((cell) => cell.info.header === 'status')?.value;
202
+ // return (
203
+ // <TableRow {...getRowProps({ row })}>
204
+ // {row.cells.map((cell) => (
205
+ // <TableCell key={cell.id}>{renderCellContent(cell, row, rowStatus)}</TableCell>
206
+ // ))}
207
+ // </TableRow>
208
+ // );
209
+ // })}
210
+ // </TableBody>
211
+ // </Table>
212
+ // {paginated && !isLoading && (
213
+ // <Pagination
214
+ // forwardText=""
215
+ // backwardText=""
216
+ // page={currentPage}
217
+ // pageSize={pageSize}
218
+ // pageSizes={pageSizes}
219
+ // totalItems={filteredClaims.length}
220
+ // size={responsiveSize}
221
+ // onChange={({ page: newPage, pageSize }) => {
222
+ // if (newPage !== currentPage) {
223
+ // goTo(newPage);
224
+ // }
225
+ // setPageSize(pageSize);
226
+ // }}
227
+ // />
228
+ // )}
229
+ // </TableContainer>
230
+ // )}
231
+ // </DataTable>
232
+ // </div>
233
+ // );
234
+ // };
235
+
236
+ // export default ClaimsManagementTable;
237
+ // const ClaimStatus = ({ row }: { row: any }) => {
238
+ // const { claims } = useFacilityClaims();
239
+ // const { t } = useTranslation();
240
+
241
+ // const claim = claims.find((claim) => claim.id === row.id);
242
+
243
+ // // Default to 'gray' if status not found in the mapping
244
+ // const tagType = statusColors[claim.status] || 'gray';
245
+
246
+ // return (
247
+ // <div className={styles.claimStatus}>
248
+ // <Tag type={tagType}>{claim.status}</Tag>
249
+ // </div>
250
+ // );
251
+ // };
252
+ import React from 'react';
253
+ import ClaimsTable from './claim-table.component';
254
+
255
+ const ClaimsManagementTable: React.FC = () => {
202
256
  return (
203
- <div className={styles.claimStatus}>
204
- <p>{cell.value}</p>
205
- {claim.externalId && <Tag type="blue">{claim.externalId}</Tag>}
206
- </div>
257
+ <ClaimsTable
258
+ title="claims"
259
+ emptyStateText="emptyClaimsState"
260
+ emptyStateHeader="emptyClaimsHeader"
261
+ includeClaimCode={true}
262
+ />
207
263
  );
208
264
  };
265
+
266
+ export default ClaimsManagementTable;
@@ -0,0 +1,111 @@
1
+ import { Button, InlineLoading, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
2
+ import { showSnackbar } from '@openmrs/esm-framework';
3
+ import React, { useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { retryClaim, updateClaimStatus } from '../../dashboard/form/claims-form.resource';
6
+ import { useFacilityClaims } from './use-facility-claims';
7
+
8
+ export const ManageClaimRequest = ({
9
+ closeModal,
10
+ claimId,
11
+ modalType = 'retry',
12
+ }: {
13
+ closeModal: () => void;
14
+ claimId: string;
15
+ modalType: 'retry' | 'update';
16
+ }) => {
17
+ const { t } = useTranslation();
18
+ const [isSubmitting, setIsSubmitting] = useState(false);
19
+ const { claims, mutate } = useFacilityClaims();
20
+
21
+ const claim = claims.find((claim) => claim.id === claimId);
22
+
23
+ const handleRetryClaim = () => {
24
+ retryClaim(claim.externalId)
25
+ .then(() => {
26
+ mutate();
27
+ showSnackbar({
28
+ kind: 'success',
29
+ title: t('success', 'Success'),
30
+ subtitle: t('succcessfullRetry', 'Claim sent successfully'),
31
+ timeoutInMs: 3000,
32
+ });
33
+ })
34
+ .catch(() => {
35
+ showSnackbar({
36
+ kind: 'error',
37
+ title: t('error', 'Error'),
38
+ subtitle: t('retryClaimError', 'Request Failed, Please try later'),
39
+ timeoutInMs: 2500,
40
+ });
41
+ })
42
+ .finally(() => {
43
+ setIsSubmitting(false);
44
+ closeModal();
45
+ });
46
+ };
47
+
48
+ const handleUpdateStatus = () => {
49
+ setIsSubmitting(true);
50
+ updateClaimStatus(claim.responseUUID)
51
+ .then(() => {
52
+ mutate();
53
+ showSnackbar({
54
+ kind: 'success',
55
+ title: t('success', 'Success'),
56
+ subtitle: t('successfulUpdate', 'Claim status updated successfully'),
57
+ timeoutInMs: 3000,
58
+ });
59
+ })
60
+ .catch(() => {
61
+ showSnackbar({
62
+ kind: 'error',
63
+ title: t('error', 'Error'),
64
+ subtitle: t('updateStatusError', 'Status update failed, Please try later'),
65
+ timeoutInMs: 2500,
66
+ });
67
+ })
68
+ .finally(() => {
69
+ setIsSubmitting(false);
70
+ closeModal();
71
+ });
72
+ };
73
+
74
+ const handleSubmit = () => {
75
+ if (modalType === 'retry') {
76
+ handleRetryClaim();
77
+ } else {
78
+ handleUpdateStatus();
79
+ }
80
+ };
81
+
82
+ return (
83
+ <React.Fragment>
84
+ <ModalHeader closeModal={closeModal}>
85
+ {modalType === 'retry' ? t('retryClaim', 'Retry Claim') : t('updateClaimStatus', 'Update Claim Status')}
86
+ </ModalHeader>
87
+ <ModalBody>
88
+ {modalType === 'retry'
89
+ ? t('retryClaimMessage', `Are you sure you want to retry making the request for ${claim.claimCode}?`)
90
+ : t('updateStatusMessage', `You are updating claim status for ${claim.claimCode}:`)}
91
+ </ModalBody>
92
+ <ModalFooter>
93
+ <Button kind="secondary" onClick={closeModal} type="button">
94
+ {t('cancel', 'Cancel')}
95
+ </Button>
96
+ <Button type="submit" onClick={handleSubmit}>
97
+ {isSubmitting ? (
98
+ <>
99
+ <InlineLoading withOverlay={false} small />
100
+ {modalType === 'retry' ? t('retrying', 'Retrying') : t('updating', 'Updating')}
101
+ </>
102
+ ) : modalType === 'retry' ? (
103
+ t('retry', 'Retry')
104
+ ) : (
105
+ t('update', 'Update')
106
+ )}
107
+ </Button>
108
+ </ModalFooter>
109
+ </React.Fragment>
110
+ );
111
+ };