@ampath/esm-dha-workflow-app 4.0.0-next.340 → 4.0.0-next.344

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.
Files changed (128) hide show
  1. package/dist/1149.js +1 -1
  2. package/dist/1171.js +1 -0
  3. package/dist/1171.js.map +1 -0
  4. package/dist/1177.js +1 -0
  5. package/dist/1177.js.map +1 -0
  6. package/dist/1239.js +1 -0
  7. package/dist/1239.js.map +1 -0
  8. package/dist/1551.js +1 -1
  9. package/dist/1560.js +1 -0
  10. package/dist/1560.js.map +1 -0
  11. package/dist/1928.js +1 -0
  12. package/dist/1928.js.map +1 -0
  13. package/dist/2093.js +1 -1
  14. package/dist/2230.js +1 -1
  15. package/dist/2493.js +1 -1
  16. package/dist/2914.js +1 -1
  17. package/dist/3078.js +1 -0
  18. package/dist/3078.js.map +1 -0
  19. package/dist/327.js +1 -0
  20. package/dist/327.js.map +1 -0
  21. package/dist/4605.js +1 -1
  22. package/dist/4611.js +1 -1
  23. package/dist/4674.js +1 -1
  24. package/dist/4674.js.map +1 -1
  25. package/dist/4986.js +1 -1
  26. package/dist/5167.js +1 -1
  27. package/dist/5317.js +1 -0
  28. package/dist/5317.js.map +1 -0
  29. package/dist/5344.js +1 -0
  30. package/dist/{2936.js.map → 5344.js.map} +1 -1
  31. package/dist/5765.js +1 -0
  32. package/dist/5765.js.map +1 -0
  33. package/dist/5851.js +1 -1
  34. package/dist/5851.js.map +1 -1
  35. package/dist/5961.js +1 -1
  36. package/dist/6264.js +1 -1
  37. package/dist/6264.js.map +1 -1
  38. package/dist/6720.js +1 -1
  39. package/dist/6942.js +1 -1
  40. package/dist/6942.js.map +1 -1
  41. package/dist/7521.js +1 -1
  42. package/dist/7521.js.map +1 -1
  43. package/dist/7549.js +1 -0
  44. package/dist/7549.js.map +1 -0
  45. package/dist/7717.js +1 -1
  46. package/dist/7857.js +1 -0
  47. package/dist/7857.js.map +1 -0
  48. package/dist/850.js +1 -0
  49. package/dist/850.js.map +1 -0
  50. package/dist/8733.js +1 -1
  51. package/dist/9070.js +1 -1
  52. package/dist/9093.js +1 -1
  53. package/dist/9093.js.map +1 -1
  54. package/dist/9338.js +1 -1
  55. package/dist/9338.js.map +1 -1
  56. package/dist/9349.js +1 -0
  57. package/dist/9349.js.map +1 -0
  58. package/dist/9428.js +1 -1
  59. package/dist/9428.js.map +1 -1
  60. package/dist/9789.js +1 -0
  61. package/dist/9789.js.map +1 -0
  62. package/dist/9871.js +1 -0
  63. package/dist/9871.js.map +1 -0
  64. package/dist/esm-dha-workflow-app.js +1 -1
  65. package/dist/esm-dha-workflow-app.js.buildmanifest.json +362 -239
  66. package/dist/esm-dha-workflow-app.js.map +1 -1
  67. package/dist/main.js +1 -1
  68. package/dist/main.js.map +1 -1
  69. package/dist/routes.json +1 -1
  70. package/package.json +1 -1
  71. package/src/billing/billing-claims.resource.ts +8 -7
  72. package/src/billing/dashboard/v2/clearance/cash.component.tsx +9 -9
  73. package/src/billing/dashboard/v2/clearance/pay-cash.component.tsx +24 -22
  74. package/src/billing/dashboard/v2/facility-bills/facility-bills.component.tsx +5 -4
  75. package/src/billing/dashboard/v2/patient-bill-details/attachments/final-bill.component.tsx +2 -2
  76. package/src/billing/dashboard/v2/patient-bill-details/attachments/generate-attachments.component.tsx +15 -4
  77. package/src/billing/dashboard/v2/patient-bill-details/attachments/invoice.component.tsx +2 -2
  78. package/src/billing/dashboard/v2/types/index.ts +6 -0
  79. package/src/billing/dashboard/v3/claim-visits/claim-intervention-details/claim-intervention-details.component.tsx +6 -1
  80. package/src/billing/dashboard/v3/claim-visits/claim-visit-details/claim-visit-details.component.tsx +5 -8
  81. package/src/billing/dashboard/v3/facility-bills/facility-bills.component.tsx +65 -62
  82. package/src/billing/dashboard/v3/patient-bill-details/attachments/case-summary/case-summary.tsx +30 -16
  83. package/src/billing/dashboard/v3/patient-bill-details/attachments/generate-attachments.component.tsx +3 -1
  84. package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.scss +4 -0
  85. package/src/billing/dashboard/v3/patient-bill-details/bill-details/bill-details.tsx +154 -156
  86. package/src/billing/dashboard/v3/patient-bill-details/claim-details/patient-claim-details.component.tsx +22 -23
  87. package/src/billing/dashboard/v3/patient-bill-details/patient-bill-details.tsx +5 -4
  88. package/src/dashboard/dashboard.component.tsx +3 -2
  89. package/src/dashboard/overview/overview.component.tsx +3 -1
  90. package/src/dashboard-meta/emt-dashboard.meta.ts +6 -0
  91. package/src/emt/cr-lookup.resource.test.ts +88 -0
  92. package/src/emt/cr-lookup.resource.ts +42 -0
  93. package/src/emt/emt-queue.component.test.tsx +324 -0
  94. package/src/emt/emt-queue.component.tsx +449 -0
  95. package/src/emt/emt-queue.scss +83 -0
  96. package/src/emt/emt.resource.test.ts +247 -0
  97. package/src/emt/emt.resource.ts +150 -0
  98. package/src/emt/handover-modal/handover-modal.component.test.tsx +273 -0
  99. package/src/emt/handover-modal/handover-modal.component.tsx +524 -0
  100. package/src/emt/handover-modal/handover-modal.scss +88 -0
  101. package/src/emt/types/emt.types.ts +137 -0
  102. package/src/index.ts +10 -0
  103. package/src/registry/registry.component.tsx +49 -1
  104. package/src/registry/utils/hie-client-adapter.ts +1 -1
  105. package/src/root.component.tsx +2 -0
  106. package/src/routes.json +25 -0
  107. package/src/side-nav-menu/nav-link-config.ts +4 -0
  108. package/dist/1362.js +0 -1
  109. package/dist/1362.js.map +0 -1
  110. package/dist/203.js +0 -1
  111. package/dist/203.js.map +0 -1
  112. package/dist/2200.js +0 -1
  113. package/dist/2200.js.map +0 -1
  114. package/dist/2936.js +0 -1
  115. package/dist/2988.js +0 -1
  116. package/dist/2988.js.map +0 -1
  117. package/dist/307.js +0 -1
  118. package/dist/307.js.map +0 -1
  119. package/dist/4008.js +0 -1
  120. package/dist/4008.js.map +0 -1
  121. package/dist/446.js +0 -1
  122. package/dist/446.js.map +0 -1
  123. package/dist/5097.js +0 -1
  124. package/dist/5097.js.map +0 -1
  125. package/dist/7240.js +0 -1
  126. package/dist/7240.js.map +0 -1
  127. package/dist/874.js +0 -1
  128. package/dist/874.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
2
  import { type PatientPayment, type PatientFacilityBillDetails, ClaimsVisit } from '../../types';
3
3
  import styles from './bill-details.scss';
4
- import { Button, OverflowMenu, OverflowMenuItem, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tag } from '@carbon/react';
4
+ import { Button, OverflowMenu, OverflowMenuItem, RadioButton, RadioButtonGroup, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tag } from '@carbon/react';
5
5
  import { formatDate, parseDate } from '@openmrs/esm-framework';
6
6
  import BillItemPaymentModal from '../modals/bill-item-payment/bill-item-payment.modal';
7
7
  import AddClaimLineModal from '../modals/add-claim-line/add-claim-line.modal';
@@ -17,13 +17,19 @@ interface billDetailsProps {
17
17
  locationUuid: string;
18
18
  claimsVisit: ClaimsVisit;
19
19
  }
20
+ type BillingScope = 'OPD' | 'INPATIENT';
20
21
  const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPayments, amrsVisitDiagnosis, consentToken, locationUuid, claimsVisit }) => {
21
22
  const [showPaymentModal, setShowPaymentModal] = useState<boolean>(false);
22
23
  const [showAddClaimLineModal, setShowAddClaimLineModal] = useState<boolean>(false);
23
24
  const [selectedBillItem, setSelectedBillItem] = useState<PatientFacilityBillDetails | null>(null);
24
25
  const setDiagnosisInterventionCode = useMemo(() => getConsultationBillIntervantionCode(), [patientBillDetails]);
26
+ const [billingScope, setBillingScope] = useState<BillingScope>('OPD');
25
27
  const invalidateProviderClaimPreview = useInvalidateProviderClaimPreview();
26
28
 
29
+ const scopedBillDetails = patientBillDetails;
30
+ const scopedPayments = patientPayments;
31
+ const scopedDiagnosis = amrsVisitDiagnosis;
32
+
27
33
  if (!patientBillDetails && !patientPayments) {
28
34
  return <>No Data</>;
29
35
  }
@@ -61,6 +67,7 @@ const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPa
61
67
  handleCloseAddClaimItemModal();
62
68
  invalidateProviderClaimPreview();
63
69
  }
70
+
64
71
  function interventionAddedToClaimLine(b: PatientFacilityBillDetails) {
65
72
  if (b.intervention_code && b.has_claim_line === 0) {
66
73
  if (claimsVisit && claimsVisit.invoices) {
@@ -73,163 +80,154 @@ const BillDetails: React.FC<billDetailsProps> = ({ patientBillDetails, patientPa
73
80
  }
74
81
  return true;
75
82
  }
83
+
84
+ const billDetailsLayout = (
85
+ <div className={styles.billDetailsLayout}>
86
+ {scopedBillDetails.length > 0 ? (
87
+ <div className={styles.billRow}>
88
+ <div>
89
+ <h6>Bill Items</h6>
90
+ </div>
91
+ <div>
92
+ <Table aria-label="bill details" size="sm">
93
+ <TableHead>
94
+ <TableRow>
95
+ <TableHeader>No</TableHeader>
96
+ <TableHeader>Bill Item</TableHeader>
97
+ <TableHeader>Intervention Code</TableHeader>
98
+ <TableHeader>Order No</TableHeader>
99
+ <TableHeader>Service Type</TableHeader>
100
+ <TableHeader>Payer</TableHeader>
101
+ <TableHeader>Payment Status</TableHeader>
102
+ <TableHeader>Quantity</TableHeader>
103
+ <TableHeader>Total</TableHeader>
104
+ <TableHeader>Actions</TableHeader>
105
+ </TableRow>
106
+ </TableHead>
107
+ <TableBody>
108
+ {scopedBillDetails.map((b, index) => (
109
+ <TableRow key={b.patient_uuid}>
110
+ <TableCell>{index + 1}</TableCell>
111
+ <TableCell>{b.billable_service}</TableCell>
112
+ <TableCell>{b.intervention_code ?? ''}</TableCell>
113
+ <TableCell>{b.order_no ?? ''}</TableCell>
114
+ <TableCell>{b.service_type ?? ''}</TableCell>
115
+ <TableCell>{b.payment_scheme}</TableCell>
116
+ <TableCell>{b.payment_status}</TableCell>
117
+ <TableCell>{b.item_quantity}</TableCell>
118
+ <TableCell>Ksh {b.item_total_price}</TableCell>
119
+ <TableCell>
120
+ {
121
+ (b.paid_status !== 'PAID' && !b.intervention_code) && <Button size="sm" kind="tertiary" onClick={() => handleBillItemPayment(b)}>Pay</Button>
122
+ }
123
+ {
124
+ b.intervention_code ?
125
+ (
126
+ claimsVisit ?
127
+ (
128
+ claimsVisit.workflow_state === 'DRAFT' ?
129
+ (!interventionAddedToClaimLine(b) && <Button size="sm" kind="tertiary" onClick={() => handleClaimLineAddition(b)}>Add Claim Line</Button>)
130
+ : <Tag>{claimsVisit.workflow_state}</Tag>
131
+ )
132
+ : <></>
133
+ )
134
+ : <></>
135
+ }
136
+ </TableCell>
137
+ </TableRow>
138
+ ))}
139
+ </TableBody>
140
+ </Table>
141
+ </div>
142
+ </div>
143
+ ) : null}
144
+
145
+ {scopedPayments.length > 0 ? (
146
+ <div className={styles.billRow}>
147
+ <div>
148
+ <h6>Bill Payments</h6>
149
+ </div>
150
+ <div>
151
+ <Table aria-label="bill details" size="sm">
152
+ <TableHead>
153
+ <TableRow>
154
+ <TableHeader>No</TableHeader>
155
+ <TableHeader>Payment Type</TableHeader>
156
+ <TableHeader>Amount</TableHeader>
157
+ <TableHeader>Amount Tendered</TableHeader>
158
+ <TableHeader>Date / Time</TableHeader>
159
+ </TableRow>
160
+ </TableHead>
161
+ <TableBody>
162
+ {scopedPayments.map((p, index) => (
163
+ <TableRow key={p.cashier_bill_payment_uuid}>
164
+ <TableCell>{index + 1}</TableCell>
165
+ <TableCell>{p.payment_mode}</TableCell>
166
+ <TableCell>Ksh {p.amount}</TableCell>
167
+ <TableCell>Ksh {p.amount_tendered}</TableCell>
168
+ <TableCell>{formatDate(parseDate(p.payment_time))}</TableCell>
169
+ </TableRow>
170
+ ))}
171
+ </TableBody>
172
+ </Table>
173
+ </div>
174
+ </div>
175
+ ) : null}
176
+
177
+ {scopedDiagnosis.length > 0 ? (
178
+ <div className={styles.billRow}>
179
+ <div>
180
+ <h6>Patient Dignosis</h6>
181
+ </div>
182
+ <div>
183
+ <VisitDiagnosisDetails
184
+ amrsVisitDiagnosis={scopedDiagnosis}
185
+ consentToken={consentToken}
186
+ locationUuid={locationUuid}
187
+ interventionCode={setDiagnosisInterventionCode}
188
+ claimsVisit={claimsVisit}
189
+ />
190
+ </div>
191
+ </div>
192
+ ) : null}
193
+ </div>
194
+ );
195
+
76
196
  return (
77
197
  <>
78
- <div className={styles.billDetailsLayout}>
79
- {patientBillDetails.length > 0 ? (
80
- <>
81
- <div className={styles.billRow}>
82
- <div>
83
- <h6>Bill Items</h6>
84
- </div>
85
- <div>
86
- <Table aria-label="bill details" size="sm">
87
- <TableHead>
88
- <TableRow>
89
- <TableHeader>No</TableHeader>
90
- <TableHeader>Bill Item</TableHeader>
91
- <TableHeader>Intervention Code</TableHeader>
92
- <TableHeader>Order No</TableHeader>
93
- <TableHeader>Service Type</TableHeader>
94
- <TableHeader>Payer</TableHeader>
95
- <TableHeader>Payment Status</TableHeader>
96
- <TableHeader>Quantity</TableHeader>
97
- <TableHeader>Total</TableHeader>
98
- <TableHeader>Actions</TableHeader>
99
- </TableRow>
100
- </TableHead>
101
- <TableBody>
102
- {patientBillDetails &&
103
- patientBillDetails.map((b, index) => {
104
- return (
105
- <>
106
- <TableRow key={b.patient_uuid}>
107
- <TableCell>{index + 1}</TableCell>
108
- <TableCell>{b.billable_service}</TableCell>
109
- <TableCell>{b.intervention_code ?? ''}</TableCell>
110
- <TableCell>{b.order_no ?? ''}</TableCell>
111
- <TableCell>{b.service_type ?? ''}</TableCell>
112
- <TableCell>{b.payment_scheme}</TableCell>
113
- <TableCell>{b.paid_status}</TableCell>
114
- <TableCell>{b.item_quantity}</TableCell>
115
- <TableCell>Ksh {b.item_total_price}</TableCell>
116
- <TableCell>
117
- {/* <OverflowMenu aria-label="overflow-menu">
118
- {
119
- (b.paid_status !== 'PAID' && !b.intervention_code) && <OverflowMenuItem itemText="Pay" onClick={() => handleBillItemPayment(b)} />
120
- }
121
- {
122
- (b.intervention_code && b.has_claim_line === 0) && <OverflowMenuItem itemText="Add Claim Line" onClick={() => handleClaimLineAddition(b)} />
123
- }
124
- </OverflowMenu> */}
125
- {
126
- (b.paid_status !== 'PAID' && !b.intervention_code) && <Button size="sm" kind="tertiary" onClick={() => handleBillItemPayment(b)}>Pay</Button>
127
- }
128
- {
129
- b.intervention_code ?
130
- (
131
- claimsVisit ?
132
- (
133
- claimsVisit.workflow_state === 'DRAFT' ?
134
- (!interventionAddedToClaimLine(b) && <Button size="sm" kind="tertiary" onClick={() => handleClaimLineAddition(b)}>Add Claim Line</Button>)
135
- : <Tag>{claimsVisit.workflow_state}</Tag>
136
- )
137
- : <></>
138
- )
139
- : <></>
140
- }
141
- </TableCell>
142
- </TableRow>
143
- </>
144
- );
145
- })}
146
- </TableBody>
147
- </Table>
148
- </div>
149
- </div>
150
- </>
151
- ) : (
152
- <></>
153
- )}
154
- {patientPayments.length > 0 ? (
155
- <>
156
- <div className={styles.billRow}>
157
- <div>
158
- <h6>Bill Payments</h6>
159
- </div>
160
- <div>
161
- <Table aria-label="bill details" size="sm">
162
- <TableHead>
163
- <TableRow>
164
- <TableHeader>No</TableHeader>
165
- <TableHeader>Payment Type</TableHeader>
166
- <TableHeader>Amount</TableHeader>
167
- <TableHeader>Amount Tendered</TableHeader>
168
- <TableHeader>Date / Time</TableHeader>
169
- </TableRow>
170
- </TableHead>
171
- <TableBody>
172
- {patientPayments &&
173
- patientPayments.map((p, index) => {
174
- return (
175
- <>
176
- <TableRow key={p.cashier_bill_payment_uuid}>
177
- <TableCell>{index + 1}</TableCell>
178
- <TableCell>{p.payment_mode}</TableCell>
179
- <TableCell>Ksh {p.amount}</TableCell>
180
- <TableCell>Ksh {p.amount_tendered}</TableCell>
181
- <TableCell>
182
- {formatDate(parseDate(p.payment_time))}</TableCell>
183
- </TableRow>
184
- </>
185
- );
186
- })}
187
- </TableBody>
188
- </Table>
189
- </div>
190
- </div>
191
- </>
192
- ) : (
193
- <></>
194
- )}
195
- {
196
- amrsVisitDiagnosis.length > 0 ? (<>
197
- <div className={styles.billRow}>
198
- <div>
199
- <h6>Patient Dignosis</h6>
200
- </div>
201
- <div>
202
- <VisitDiagnosisDetails
203
- amrsVisitDiagnosis={amrsVisitDiagnosis}
204
- consentToken={consentToken}
205
- locationUuid={locationUuid}
206
- interventionCode={setDiagnosisInterventionCode}
207
- claimsVisit={claimsVisit}
208
- />
209
- </div>
210
- </div>
198
+ <RadioButtonGroup
199
+ className={styles.radioGroup}
200
+ name="billing-scope"
201
+ valueSelected={billingScope}
202
+ onChange={(value) => setBillingScope(value as BillingScope)}
203
+ invalidText="Invalid selection"
204
+ warnText="Please notice the warning"
205
+ >
206
+ <RadioButton id="outpatient" labelText="OPD" value="OPD" />
207
+ <RadioButton id="inpatient" labelText="INPATIENT" value="INPATIENT" />
208
+ </RadioButtonGroup>
211
209
 
212
- </>) : (<></>)
213
- }
214
- {
215
- (showPaymentModal && selectedBillItem) && <BillItemPaymentModal
216
- open={showPaymentModal}
217
- billItem={selectedBillItem}
218
- onClose={handleClosePayModal}
219
- onPay={handleSuccessfullPayment} />
220
- }
221
- {
222
- (showAddClaimLineModal && selectedBillItem) && <AddClaimLineModal
223
- open={showAddClaimLineModal}
224
- billItem={selectedBillItem}
225
- onClose={handleCloseAddClaimItemModal}
226
- onSuccess={onSuccess}
227
- locationUuid={locationUuid}
228
- consentToken={consentToken}
229
- />
230
- }
231
- </div>
232
- </>
233
- );
210
+ {billDetailsLayout}
211
+
212
+ {showPaymentModal && selectedBillItem && (
213
+ <BillItemPaymentModal
214
+ open={showPaymentModal}
215
+ billItem={selectedBillItem}
216
+ onClose={handleClosePayModal}
217
+ onPay={handleSuccessfullPayment}
218
+ />
219
+ )}
220
+ {showAddClaimLineModal && selectedBillItem && (
221
+ <AddClaimLineModal
222
+ open={showAddClaimLineModal}
223
+ billItem={selectedBillItem}
224
+ onClose={handleCloseAddClaimItemModal}
225
+ onSuccess={onSuccess}
226
+ locationUuid={locationUuid}
227
+ consentToken={consentToken}
228
+ />
229
+ )}
230
+ </>
231
+ );
234
232
  };
235
233
  export default BillDetails;
@@ -1,17 +1,18 @@
1
- import React, { useEffect } from "react";
2
- import { useState } from "react";
3
- import { type ProviderClaimPreviewDto, type ClaimsVisit, type PatientFacilityBillDetails } from "../../types";
4
- import { showSnackbar } from "@openmrs/esm-framework";
5
- import { fetchProviderClaimPreview, useProviderClaimPreview } from "../../../../billing-claims.resource";
1
+ import React, { useEffect } from 'react';
2
+ import { useState } from 'react';
3
+ import { type ProviderClaimPreviewDto, type ClaimsVisit, type PatientFacilityBillDetails } from '../../types';
4
+ import { showSnackbar } from '@openmrs/esm-framework';
5
+ import { fetchProviderClaimPreview, useProviderClaimPreview } from '../../../../billing-claims.resource';
6
6
  import styles from './patient-claim-details.component.scss';
7
- import ClaimVisitDetails from "../../claim-visits/claim-visit-details/claim-visit-details.component";
8
- import { InlineLoading } from "@carbon/react";
7
+ import ClaimVisitDetails from '../../claim-visits/claim-visit-details/claim-visit-details.component';
8
+ import { InlineLoading } from '@carbon/react';
9
9
 
10
10
  interface patientClaimDetailsProps {
11
11
  consentToken: string;
12
12
  locationUuid: string;
13
13
  patientBillDetails: PatientFacilityBillDetails[];
14
14
  onBillDetailsChange?: () => void;
15
+ billingDate: string;
15
16
  onLoadingClaimVisit?: (claimVisit: ClaimsVisit) => void;
16
17
  }
17
18
  const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
@@ -19,6 +20,7 @@ const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
19
20
  locationUuid,
20
21
  patientBillDetails,
21
22
  onBillDetailsChange,
23
+ billingDate,
22
24
  onLoadingClaimVisit
23
25
  }) => {
24
26
  const [patientBill, setPatientBill] = useState<PatientFacilityBillDetails>();
@@ -37,33 +39,30 @@ const PatientClaimDetails: React.FC<patientClaimDetailsProps> = ({
37
39
  }, [consentToken, locationUuid]);
38
40
 
39
41
  if (isLoading && !claimVisit) {
40
- return <InlineLoading description='Loading data...please wait' />
42
+ return <InlineLoading description="Loading data...please wait" />;
41
43
  }
42
44
 
43
45
  function getPatientBill() {
44
- const bill = patientBillDetails.find(details => details.consent_token === consentToken);
46
+ const bill = patientBillDetails.find((details) => details.consent_token === consentToken);
45
47
  setPatientBill(bill);
46
48
  }
47
49
 
48
- return <>
49
- {
50
- isValidating &&
51
- <InlineLoading description='Refreshing data...' />
52
- }
53
- <div className={styles.pcLayout}>
54
- {
55
- claimVisit && (
50
+ return (
51
+ <>
52
+ {isValidating && <InlineLoading description="Refreshing data..." />}
53
+ <div className={styles.pcLayout}>
54
+ {claimVisit && (
56
55
  <ClaimVisitDetails
57
56
  patientBillDetails={patientBill}
58
57
  claimsVisit={claimVisit}
59
58
  locationUuid={locationUuid}
60
59
  onBillDetailsChange={onBillDetailsChange}
61
60
  claimRefreshing={isValidating}
61
+ billingDate={billingDate}
62
62
  />
63
- )
64
- }
65
-
66
- </div>
67
- </>
63
+ )}
64
+ </div>
65
+ </>
66
+ );
68
67
  };
69
- export default PatientClaimDetails;
68
+ export default PatientClaimDetails;
@@ -139,7 +139,7 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
139
139
  return s.paid_status === 'POSTED';
140
140
  });
141
141
  if (hasPostedBill) {
142
- return 'PARTIALLY PAID'
142
+ return 'PARTIALLY PAID';
143
143
  }
144
144
  const hasPendingBill = patientBillDetails.some((s) => {
145
145
  return s.paid_status === 'PENDING';
@@ -208,13 +208,13 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
208
208
  return {
209
209
  patientUuid: patientUuid,
210
210
  visitDate: billingDate,
211
- locationUuid: locationUuid
211
+ locationUuid: locationUuid,
212
212
  };
213
213
  }
214
214
  function getPatientAmrsMaternityDiagnosisPayload(): AmrsMaternityDiagnosisDto {
215
215
  return {
216
216
  patientUuid: patientUuid,
217
- billingDate: billingDate
217
+ billingDate: billingDate,
218
218
  };
219
219
  }
220
220
  function onLoadingClaimVisit(claimVisit: ClaimsVisit) {
@@ -324,6 +324,7 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
324
324
  patientBillDetails={patientBillDetails}
325
325
  consentToken={consentToken}
326
326
  onBillDetailsChange={getPatientBillDetails}
327
+ billingDate={billingDate}
327
328
  onLoadingClaimVisit={onLoadingClaimVisit}
328
329
  />
329
330
  </>
@@ -339,4 +340,4 @@ const PatientBillDetails: React.FC<patientBillDetailsProps> = ({ patientUuid, lo
339
340
  );
340
341
  };
341
342
 
342
- export default PatientBillDetails;
343
+ export default PatientBillDetails;
@@ -3,12 +3,11 @@ import { Analytics } from '@carbon/react/icons';
3
3
 
4
4
  import styles from './dashboard.component.scss';
5
5
  import Overview from './overview/overview.component';
6
- import { getServiceQueueByLocationUuid } from '../service-queues/service-queues.resource';
7
6
  import { type QueueEntryResult } from '../registry/types';
8
7
  import { useSession } from '@openmrs/esm-framework';
9
- import { QUEUE_SERVICE_UUIDS } from '../shared/constants/concepts';
10
8
  import { getDashBoardSummary } from '../resources/dashboard.resource';
11
9
  import FacilityAndWorkerSlot from '../shared/ui/facility-worker-slot/facility-worker.component-slot.component';
10
+ import { usePendingReferrals } from '../emt/emt.resource';
12
11
 
13
12
  interface DashboardProps {}
14
13
 
@@ -18,6 +17,7 @@ const Dashboard: React.FC<DashboardProps> = () => {
18
17
  const [dashboardSummary, setDashboardSummary] = useState<any[]>([]);
19
18
  const session = useSession();
20
19
  const locationUuid = session.sessionLocation.uuid;
20
+ const { count: emtCount } = usePendingReferrals(1, 0);
21
21
  useEffect(() => {
22
22
  getDashBoardData();
23
23
  }, []);
@@ -45,6 +45,7 @@ const Dashboard: React.FC<DashboardProps> = () => {
45
45
  triageCount={triageQueueEntries}
46
46
  consultationCount={consultationQueueEntries}
47
47
  dashboardSummary={dashboardSummary}
48
+ emtCount={emtCount}
48
49
  />
49
50
  </div>
50
51
  );
@@ -16,9 +16,10 @@ interface OverviewProps {
16
16
  triageCount?: QueueEntryResult[];
17
17
  consultationCount?: QueueEntryResult[];
18
18
  dashboardSummary?: any;
19
+ emtCount?: number;
19
20
  }
20
21
 
21
- const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, dashboardSummary }) => {
22
+ const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, dashboardSummary, emtCount }) => {
22
23
  const totalPatients: QueueEntryResult[] = [...triageCount, ...consultationCount];
23
24
  const patientsInQueue = totalPatients.filter(
24
25
  (patient) => patient.status === 'WAITING' || patient.status === 'IN SERVICE',
@@ -72,6 +73,7 @@ const Overview: React.FC<OverviewProps> = ({ triageCount, consultationCount, das
72
73
  { key: 'labs', title: 'Labs', unit: 'Orders', value: dashboardSummary?.labs ?? 0 },
73
74
  { key: 'pharmacy', title: 'Pharmacy', unit: 'Orders', value: dashboardSummary?.pharmacy ?? 0 },
74
75
  { key: 'emergencies', title: 'Emergencies', unit: 'Patients', value: dashboardSummary?.emergencies ?? 0 },
76
+ { key: 'emt', title: 'EMT Referrals', unit: 'Pending', value: emtCount ?? 0 },
75
77
  {
76
78
  key: 'waiting',
77
79
  title: 'Avg. Waiting Time',
@@ -0,0 +1,6 @@
1
+ export const emtDashboardMeta = {
2
+ path: 'emt',
3
+ slot: 'emt-queue-dashboard-slot',
4
+ title: 'EMT / Referral',
5
+ basePath: `${window.spaBase}/home`,
6
+ };
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Tests for the CR-by-crId lookup helper and clientDisplayName utility.
3
+ */
4
+ jest.mock('../registry/registry.resource', () => ({
5
+ fetchClientRegistryData: jest.fn(),
6
+ }));
7
+
8
+ import { fetchClientRegistryData } from '../registry/registry.resource';
9
+ import { fetchClientByCrId, clientDisplayName } from './cr-lookup.resource';
10
+
11
+ const mockFetchClientRegistryData = jest.mocked(fetchClientRegistryData);
12
+
13
+ beforeEach(() => {
14
+ jest.resetAllMocks();
15
+ });
16
+
17
+ describe('fetchClientByCrId', () => {
18
+ it('searches the client registry by CR id and returns the first match', async () => {
19
+ const client = {
20
+ id: 'CR5617849204955-8',
21
+ first_name: 'Jane',
22
+ middle_name: '',
23
+ last_name: 'Doe',
24
+ gender: 'F',
25
+ date_of_birth: '1990-01-15',
26
+ identification_type: 'id',
27
+ identification_number: 'CR5617849204955-8',
28
+ };
29
+ mockFetchClientRegistryData.mockResolvedValueOnce([client]);
30
+
31
+ const result = await fetchClientByCrId('CR5617849204955-8', 'loc-uuid-1');
32
+
33
+ expect(mockFetchClientRegistryData).toHaveBeenCalledWith({
34
+ identificationNumber: 'CR5617849204955-8',
35
+ identificationType: 'CR id',
36
+ locationUuid: 'loc-uuid-1',
37
+ });
38
+ expect(result?.first_name).toBe('Jane');
39
+ });
40
+
41
+ it('returns null when the search finds no matching client', async () => {
42
+ mockFetchClientRegistryData.mockResolvedValueOnce([]);
43
+ const result = await fetchClientByCrId('CR-MISSING', 'loc-uuid-1');
44
+ expect(result).toBeNull();
45
+ });
46
+
47
+ it('returns null when the search fails (graceful degradation)', async () => {
48
+ mockFetchClientRegistryData.mockRejectedValueOnce(new Error('Request failed with 404: Not found'));
49
+ const result = await fetchClientByCrId('CR-MISSING', 'loc-uuid-1');
50
+ expect(result).toBeNull();
51
+ });
52
+
53
+ it('returns null for an empty cr_id without searching', async () => {
54
+ const result = await fetchClientByCrId('', 'loc-uuid-1');
55
+ expect(result).toBeNull();
56
+ expect(mockFetchClientRegistryData).not.toHaveBeenCalled();
57
+ });
58
+
59
+ it('returns null for a missing location uuid without searching', async () => {
60
+ const result = await fetchClientByCrId('CR5617849204955-8', '');
61
+ expect(result).toBeNull();
62
+ expect(mockFetchClientRegistryData).not.toHaveBeenCalled();
63
+ });
64
+ });
65
+
66
+ describe('clientDisplayName', () => {
67
+ it('joins first, middle, last names', () => {
68
+ const client = {
69
+ first_name: 'Jane',
70
+ middle_name: 'Akinyi',
71
+ last_name: 'Doe',
72
+ } as any;
73
+ expect(clientDisplayName(client, 'CR123')).toBe('Jane Akinyi Doe');
74
+ });
75
+
76
+ it('skips missing name parts', () => {
77
+ const client = {
78
+ first_name: 'Jane',
79
+ middle_name: '',
80
+ last_name: 'Doe',
81
+ } as any;
82
+ expect(clientDisplayName(client, 'CR123')).toBe('Jane Doe');
83
+ });
84
+
85
+ it('falls back to cr_id when client is null', () => {
86
+ expect(clientDisplayName(null, 'CR123')).toBe('CR123');
87
+ });
88
+ });
@@ -0,0 +1,42 @@
1
+ import { fetchClientRegistryData } from '../registry/registry.resource';
2
+ import { HieIdentificationType, type HieClient } from '../registry/types';
3
+
4
+ /**
5
+ * Client Registry lookup for an EMT referral.
6
+ *
7
+ * The referral only carries a `cr_id` (the CR record's id). We fetch the full
8
+ * CR record so the queue can show a human-readable patient (name, sex, DOB,
9
+ * national ID, …) instead of a bare identifier.
10
+ *
11
+ * The CR is only queryable through its search endpoint, so we reuse the
12
+ * established `fetchClientRegistryData` search with
13
+ * `identificationType: CR_SEARCH_IDENTIFICATION_TYPE` ('CR id'). The search
14
+ * returns an array; a CR id is unique, so the first entry is the record we want.
15
+ *
16
+ * Lookups are done lazily per-row (on expand / on render of a small page) so a
17
+ * single failed or slow CR fetch never blocks the whole queue.
18
+ */
19
+ export async function fetchClientByCrId(crId: string, locationUuid: string): Promise<HieClient | null> {
20
+ if (!crId || !locationUuid) return null;
21
+ try {
22
+ const result = await fetchClientRegistryData({
23
+ identificationNumber: crId,
24
+ identificationType: HieIdentificationType.Cr,
25
+ locationUuid,
26
+ });
27
+ return Array.isArray(result) && result.length > 0 ? (result[0] as HieClient) : null;
28
+ } catch {
29
+ // Graceful degradation: a missing CR record must not break the queue row.
30
+ // Callers surface "patient details unavailable" and keep the cr_id visible.
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /** Display name from a CR record, falling back gracefully. */
36
+ export function clientDisplayName(client: HieClient | null | undefined, crId: string): string {
37
+ if (!client) return crId;
38
+ return [client.first_name, client.middle_name, client.last_name]
39
+ .filter(Boolean)
40
+ .join(' ')
41
+ .trim() || crId;
42
+ }