@kenyaemr/esm-billing-app 5.4.1-pre.2093 → 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,237 +1,15 @@
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 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
- <div className={styles.dataTableSkeleton}>
120
- <div className={styles.tableHeader}>
121
- <CardHeader title={t('preathsRequests', 'Preauth Requests')}>{''}</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 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
- selectedItem={providers?.find((p) => p?.uuid === field.value)}
421
+ initialSelectedItem={
422
+ field.value && providers ? providers.find((p) => p.uuid === field.value) : null
423
+ }
423
424
  onChange={({ selectedItem }) => {
424
- field.onChange(selectedItem?.display?.split('-')?.at(-1)?.trim() ?? '');
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 = (externalId: string) => {
95
- const url = `/ws/rest/v1/insuranceclaims/claim/update-status?externalId=${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
  });
@@ -0,0 +1,7 @@
1
+ export const statusColors = {
2
+ ENTERED: 'blue',
3
+ ERRORED: 'red',
4
+ REJECTED: 'red',
5
+ CHECKED: 'green',
6
+ VALUATED: 'purple',
7
+ };
@@ -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
+ }