@kenyaemr/esm-billing-app 5.4.1-pre.2093 → 5.4.1-pre.2099
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.
- package/.turbo/turbo-build.log +86 -86
- package/dist/{946.js → 912.js} +1 -1
- package/dist/912.js.map +1 -0
- package/dist/kenyaemr-esm-billing-app.js +1 -1
- package/dist/kenyaemr-esm-billing-app.js.buildmanifest.json +9 -9
- package/dist/main.js +3 -3
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/claims/claims-management/table/claim-table.component.tsx +262 -0
- package/src/claims/claims-management/table/claims-list-table.component.tsx +259 -232
- package/src/claims/claims-management/table/manage-claim-request.modal.tsx +3 -3
- package/src/claims/claims-management/table/preauth-table.tmp.component.tsx +8 -230
- package/src/claims/claims-management/table/use-facility-claims.ts +1 -1
- package/src/claims/dashboard/form/claims-form.component.tsx +7 -3
- package/src/claims/dashboard/form/claims-form.resource.ts +2 -2
- package/src/claims/utils.ts +7 -0
- package/src/types/index.ts +38 -0
- package/dist/946.js.map +0 -1
|
@@ -1,237 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
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 React from 'react';
|
|
2
|
+
import ClaimsTable from './claim-table.component';
|
|
24
3
|
|
|
25
4
|
const PreauthTableTemporary: 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
|
-
{ value: 'APPROVED', label: t('approved', 'Approved') },
|
|
40
|
-
],
|
|
41
|
-
[t],
|
|
42
|
-
);
|
|
43
|
-
const [pageSize, setPageSize] = useState(5);
|
|
44
|
-
const filterClaims = (claim) => {
|
|
45
|
-
const status = filters?.status;
|
|
46
|
-
const search = filters?.search?.toLowerCase();
|
|
47
|
-
const fromDate = filters?.fromDate ? new Date(filters.fromDate) : null;
|
|
48
|
-
const toDate = filters?.toDate ? new Date(filters.toDate) : null;
|
|
49
|
-
|
|
50
|
-
const patientName = claim.patientName?.toLowerCase();
|
|
51
|
-
const providerName = claim.providerName?.toLowerCase();
|
|
52
|
-
const timeStamp = new Date(claim.dateFrom);
|
|
53
|
-
const preauthStatus = claim.status;
|
|
54
|
-
|
|
55
|
-
// Check status (allow 'all' to bypass the filter)
|
|
56
|
-
const statusMatch = status === 'all' || preauthStatus === status;
|
|
57
|
-
|
|
58
|
-
// Check search filter (patient name or provider name contains the search term)
|
|
59
|
-
const searchMatch = !search || patientName?.includes(search) || providerName?.includes(search);
|
|
60
|
-
|
|
61
|
-
// Check date range filter (timestamp is between fromDate and toDate if provided)
|
|
62
|
-
const dateMatch = (!fromDate || timeStamp >= fromDate) && (!toDate || timeStamp <= toDate);
|
|
63
|
-
|
|
64
|
-
// Return true if all conditions match
|
|
65
|
-
return statusMatch && searchMatch && dateMatch;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const filteredClaims = claims.filter(filterClaims);
|
|
69
|
-
const { paginated, goTo, results, currentPage } = usePagination(filteredClaims, pageSize);
|
|
70
|
-
const { pageSizes } = usePaginationInfo(pageSize, claims.length, currentPage, results.length);
|
|
71
|
-
const responsiveSize = isDesktop(useLayoutType()) ? 'sm' : 'lg';
|
|
72
|
-
const layout = useLayoutType();
|
|
73
|
-
const size = layout === 'tablet' ? 'lg' : 'md';
|
|
74
|
-
|
|
75
|
-
const headers = [
|
|
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('emptyPreauthState', 'There are no preauth to display')}
|
|
105
|
-
headerTitle={t('emptyPreauthHeader', 'No Preauths')}
|
|
106
|
-
/>
|
|
107
|
-
</div>
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
const handleClaimAction = (claimId: string, modalType: 'retry' | 'update') => {
|
|
111
|
-
const dispose = showModal('manage-claim-request-modal', {
|
|
112
|
-
closeModal: () => dispose(),
|
|
113
|
-
claimId,
|
|
114
|
-
modalType,
|
|
115
|
-
});
|
|
116
|
-
};
|
|
117
|
-
|
|
118
5
|
return (
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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 row={row} />
|
|
153
|
-
) : cell.info.header === 'action' ? (
|
|
154
|
-
<OverflowMenu size={size} flipped>
|
|
155
|
-
{['ENTERED', 'ERRORED'].includes(rowStatus) && (
|
|
156
|
-
<OverflowMenuItem
|
|
157
|
-
itemText={t('retryRequest', 'Retry request')}
|
|
158
|
-
onClick={() => handleClaimAction(row.id, 'retry')}
|
|
159
|
-
/>
|
|
160
|
-
)}
|
|
161
|
-
<OverflowMenuItem
|
|
162
|
-
itemText={t('updateStatus', 'Update status')}
|
|
163
|
-
onClick={() => handleClaimAction(row.id, 'update')}
|
|
164
|
-
/>
|
|
165
|
-
</OverflowMenu>
|
|
166
|
-
) : cell.info.header === 'dateFrom' ? (
|
|
167
|
-
formatDate(parseDate(cell.value))
|
|
168
|
-
) : cell.info.header === 'approvedTotal' ? (
|
|
169
|
-
['APPROVED'].includes(rowStatus) ? (
|
|
170
|
-
row.cells.find((c) => c.info.header === 'claimedTotal')?.value || cell.value
|
|
171
|
-
) : (
|
|
172
|
-
cell.value
|
|
173
|
-
)
|
|
174
|
-
) : (
|
|
175
|
-
cell.value
|
|
176
|
-
)}
|
|
177
|
-
</TableCell>
|
|
178
|
-
))}
|
|
179
|
-
</TableRow>
|
|
180
|
-
);
|
|
181
|
-
})}
|
|
182
|
-
</TableBody>
|
|
183
|
-
</Table>
|
|
184
|
-
{paginated && !isLoading && (
|
|
185
|
-
<Pagination
|
|
186
|
-
forwardText=""
|
|
187
|
-
backwardText=""
|
|
188
|
-
page={currentPage}
|
|
189
|
-
pageSize={pageSize}
|
|
190
|
-
pageSizes={pageSizes}
|
|
191
|
-
totalItems={filteredClaims.length}
|
|
192
|
-
size={responsiveSize}
|
|
193
|
-
onChange={({ page: newPage, pageSize }) => {
|
|
194
|
-
if (newPage !== currentPage) {
|
|
195
|
-
goTo(newPage);
|
|
196
|
-
}
|
|
197
|
-
setPageSize(pageSize);
|
|
198
|
-
}}
|
|
199
|
-
/>
|
|
200
|
-
)}
|
|
201
|
-
</TableContainer>
|
|
202
|
-
)}
|
|
203
|
-
</DataTable>
|
|
204
|
-
</div>
|
|
6
|
+
<ClaimsTable
|
|
7
|
+
title="preauthsRequests"
|
|
8
|
+
emptyStateText="emptyPreauthState"
|
|
9
|
+
emptyStateHeader="emptyPreauthHeader"
|
|
10
|
+
includeClaimCode={false}
|
|
11
|
+
/>
|
|
205
12
|
);
|
|
206
13
|
};
|
|
207
14
|
|
|
208
15
|
export default PreauthTableTemporary;
|
|
209
|
-
const ClaimStatus = ({ row }: { row: any }) => {
|
|
210
|
-
const { claims } = useFacilityClaims();
|
|
211
|
-
const { t } = useTranslation();
|
|
212
|
-
|
|
213
|
-
const claim = claims.find((claim) => claim.id === row.id);
|
|
214
|
-
|
|
215
|
-
const getTagType = (status) => {
|
|
216
|
-
switch (status) {
|
|
217
|
-
case 'ENTERED':
|
|
218
|
-
return 'blue';
|
|
219
|
-
case 'ERRORED':
|
|
220
|
-
return 'red';
|
|
221
|
-
case 'REJECTED':
|
|
222
|
-
return 'red';
|
|
223
|
-
case 'CHECKED':
|
|
224
|
-
return 'green';
|
|
225
|
-
case 'VALUATED':
|
|
226
|
-
return 'purple';
|
|
227
|
-
default:
|
|
228
|
-
return 'gray';
|
|
229
|
-
}
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
return (
|
|
233
|
-
<div className={styles.claimStatus}>
|
|
234
|
-
<Tag type={getTagType(claim.status)}>{claim.status}</Tag>
|
|
235
|
-
</div>
|
|
236
|
-
);
|
|
237
|
-
};
|
|
@@ -4,7 +4,7 @@ import { FacilityClaim } from '../../../types';
|
|
|
4
4
|
|
|
5
5
|
export const useFacilityClaims = () => {
|
|
6
6
|
const customPresentation =
|
|
7
|
-
'custom:(uuid,claimCode,dateFrom,dateTo,claimedTotal,approvedTotal,status,externalId,provider:(display),patient:(display))';
|
|
7
|
+
'custom:(uuid,claimCode,dateFrom,dateTo,claimedTotal,approvedTotal,status,externalId,responseUUID,provider:(display),patient:(display))';
|
|
8
8
|
const url = `${restBaseUrl}/claim?v=${customPresentation}`;
|
|
9
9
|
|
|
10
10
|
const { data, error, isLoading, mutate, isValidating } = useSWR<FetchResponse<{ results: Array<FacilityClaim> }>>(
|
|
@@ -411,7 +411,6 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
411
411
|
name="provider"
|
|
412
412
|
render={({ field }) => (
|
|
413
413
|
<ComboBox
|
|
414
|
-
{...field}
|
|
415
414
|
id="provider"
|
|
416
415
|
invalid={shouldShowError('provider')}
|
|
417
416
|
invalidText={errors.provider?.message}
|
|
@@ -419,11 +418,16 @@ const ClaimsForm: React.FC<ClaimsFormProps> = ({ bill, selectedLineItems }) => {
|
|
|
419
418
|
titleText={t('provider', 'Provider')}
|
|
420
419
|
items={providers}
|
|
421
420
|
itemToString={(item) => item?.display?.split('-')?.at(-1)?.trim() ?? ''}
|
|
422
|
-
|
|
421
|
+
initialSelectedItem={
|
|
422
|
+
field.value && providers ? providers.find((p) => p.uuid === field.value) : null
|
|
423
|
+
}
|
|
423
424
|
onChange={({ selectedItem }) => {
|
|
424
|
-
field.onChange(selectedItem?.
|
|
425
|
+
field.onChange(selectedItem?.uuid || '');
|
|
425
426
|
setValidationEnabled(true);
|
|
426
427
|
}}
|
|
428
|
+
name={field.name}
|
|
429
|
+
onBlur={field.onBlur}
|
|
430
|
+
ref={field.ref}
|
|
427
431
|
/>
|
|
428
432
|
)}
|
|
429
433
|
/>
|
|
@@ -91,8 +91,8 @@ export const usePackages = () => {
|
|
|
91
91
|
};
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
export const updateClaimStatus = (
|
|
95
|
-
const url = `/ws/rest/v1/insuranceclaims/claim/update-status?externalId=${
|
|
94
|
+
export const updateClaimStatus = (responseUUID: string) => {
|
|
95
|
+
const url = `/ws/rest/v1/insuranceclaims/claim/update-status?externalId=${responseUUID}`;
|
|
96
96
|
return openmrsFetch(url, {
|
|
97
97
|
method: 'GET',
|
|
98
98
|
});
|
package/src/types/index.ts
CHANGED
|
@@ -548,6 +548,7 @@ export type FacilityClaim = {
|
|
|
548
548
|
} | null;
|
|
549
549
|
patient?: { display: string };
|
|
550
550
|
externalId: string;
|
|
551
|
+
responseUUID: string;
|
|
551
552
|
};
|
|
552
553
|
export type BillingPromptType = 'patient-chart' | 'billing-orders';
|
|
553
554
|
|
|
@@ -568,3 +569,40 @@ export interface Filter {
|
|
|
568
569
|
cashiers?: Array<string>;
|
|
569
570
|
status?: string;
|
|
570
571
|
}
|
|
572
|
+
export interface DataTableRow {
|
|
573
|
+
id: string;
|
|
574
|
+
cells: Array<Cell>;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export interface Cell {
|
|
578
|
+
id: string;
|
|
579
|
+
value: any;
|
|
580
|
+
info: Info;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
export interface Info {
|
|
584
|
+
header: string;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
export interface Claim {
|
|
588
|
+
id: string;
|
|
589
|
+
status: string;
|
|
590
|
+
claimCode?: string;
|
|
591
|
+
providerName?: string;
|
|
592
|
+
patientName?: string;
|
|
593
|
+
claimedTotal?: number | string;
|
|
594
|
+
approvedTotal?: number | string;
|
|
595
|
+
dateFrom: string;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
export interface Header {
|
|
599
|
+
key: string;
|
|
600
|
+
header: string;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export interface TableProps {
|
|
604
|
+
title: string;
|
|
605
|
+
emptyStateText: string;
|
|
606
|
+
emptyStateHeader: string;
|
|
607
|
+
includeClaimCode?: boolean;
|
|
608
|
+
}
|