@ampath/esm-dha-workflow-app 4.0.0-claim-workflows.428 → 4.0.0-claim-workflows.430

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,4 +1,4 @@
1
- import React, { useRef, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import {
4
4
  showSnackbar,
@@ -7,6 +7,7 @@ import {
7
7
  type Workspace2DefinitionProps,
8
8
  } from '@openmrs/esm-framework';
9
9
  import { ApplicableDocumentType, type VisitIntervention } from '../../types';
10
+ import { useDeathNotification } from './death-notification.resource';
10
11
 
11
12
  import styles from './attachments.scss';
12
13
  import { Button, Form, InlineLoading, Modal, Tag } from '@carbon/react';
@@ -25,6 +26,7 @@ import GeneralDischargeSummary from '../../../v3/patient-bill-details/attachment
25
26
  import LabOrdersComponent from '../../../v3/patient-bill-details/attachments/lab-results/lab-orders.component';
26
27
  import ProformaInvoiceComponent from '../../../v3/patient-bill-details/attachments/proforma-invoice/proforma-invoice.component';
27
28
  import UltrasoundReport from '../../../v3/patient-bill-details/attachments/ultrasound-report/utlrasound-report.component';
29
+ import DeathNotification from './death-notification.component';
28
30
 
29
31
  interface GenerateAttachmentsProps {
30
32
  claimInterventions: VisitIntervention;
@@ -41,10 +43,14 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
41
43
  const { claimInterventions, bill, consentToken, patientUuid, billingDate } =
42
44
  workspaceProps ?? ({} as GenerateAttachmentsProps);
43
45
  const { t } = useTranslation();
46
+ const {
47
+ patient: deathNotificationPatient,
48
+ encounter: deathNotificationEncounter,
49
+ isLoading: isLoadingDeathNotification,
50
+ } = useDeathNotification(patientUuid);
44
51
  const [previewUrl, setPreviewUrl] = useState<string>();
45
52
  const [previewOpen, setPreviewOpen] = useState(false);
46
53
  const [generatingDocumentId, setGeneratingDocumentId] = useState<string | null>(null);
47
-
48
54
  const [documents, setDocuments] = useState<GeneratedDocument[]>(() => {
49
55
  const uploadOnlyDocumentTypes = new Set([
50
56
  ApplicableDocumentType.CLAIM_FORM,
@@ -53,7 +59,8 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
53
59
  const uniqueDocumentTypes = [
54
60
  ...new Set(
55
61
  (claimInterventions?.applicable_document_types ?? []).filter(
56
- (documentType) => !uploadOnlyDocumentTypes.has(documentType.trim().toUpperCase() as ApplicableDocumentType),
62
+ (documentType) =>
63
+ !uploadOnlyDocumentTypes.has(documentType.trim().toUpperCase() as ApplicableDocumentType),
57
64
  ),
58
65
  ),
59
66
  ];
@@ -77,6 +84,27 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
77
84
  const labResultsRef = useRef<HTMLDivElement>(null);
78
85
  const proformaInvoiceRef = useRef<HTMLDivElement>(null);
79
86
  const ultrasoundReportRef = useRef<HTMLDivElement>(null);
87
+ const deathNotificationRef = useRef<HTMLDivElement>(null);
88
+
89
+ useEffect(() => {
90
+ if (!deathNotificationEncounter) {
91
+ return;
92
+ }
93
+
94
+ setDocuments((previous) =>
95
+ previous.some((document) => document.name.trim().toUpperCase() === ApplicableDocumentType.DEATH_NOTICE)
96
+ ? previous
97
+ : [
98
+ ...previous,
99
+ {
100
+ id: crypto.randomUUID(),
101
+ name: ApplicableDocumentType.DEATH_NOTICE,
102
+ generated: false,
103
+ uploaded: false,
104
+ },
105
+ ],
106
+ );
107
+ }, [deathNotificationEncounter]);
80
108
 
81
109
  if (!claimInterventions) return null;
82
110
 
@@ -149,9 +177,9 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
149
177
  prev.map((d) =>
150
178
  d.id === document.id
151
179
  ? {
152
- ...d,
153
- uploaded: true,
154
- }
180
+ ...d,
181
+ uploaded: true,
182
+ }
155
183
  : d,
156
184
  ),
157
185
  );
@@ -224,6 +252,10 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
224
252
  element = ultrasoundReportRef.current;
225
253
  break;
226
254
 
255
+ case ApplicableDocumentType.DEATH_NOTICE:
256
+ element = deathNotificationRef.current;
257
+ break;
258
+
227
259
  default:
228
260
  console.warn(`${document.name} cannot be generated. Kindly upload it instead using the upload button.`);
229
261
  showSnackbar({
@@ -251,12 +283,12 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
251
283
  prev.map((d) =>
252
284
  d.id === document.id
253
285
  ? {
254
- ...d,
255
- generated: true,
256
- uploaded: false,
257
- file,
258
- url,
259
- }
286
+ ...d,
287
+ generated: true,
288
+ uploaded: false,
289
+ file,
290
+ url,
291
+ }
260
292
  : d,
261
293
  ),
262
294
  );
@@ -267,132 +299,149 @@ const GenerateAttachments: React.FC<Workspace2DefinitionProps<GenerateAttachment
267
299
  return (
268
300
  <Workspace2 title={t('generateAttachments', 'Generate Attachments')}>
269
301
  <>
270
- <Form className={styles.form}>
271
- <div className={styles.formContent}>
272
- <div
273
- style={{
274
- display: 'flex',
275
- alignItems: 'center',
276
- gap: '0.75rem',
277
- margin: '1rem',
278
- justifyContent: 'center',
279
- }}
280
- >
281
- <div className={styles.interventionSection}>
282
- <span>Intervention name</span>
283
- <Tag type="blue">{claimInterventions.intervention_name}</Tag>
284
- </div>
285
- <div className={styles.interventionSection}>
286
- <span>Intervention code</span>
287
- <Tag type="blue">{claimInterventions.intervention_code}</Tag>
302
+ <Form className={styles.form}>
303
+ <div className={styles.formContent}>
304
+ <div
305
+ style={{
306
+ display: 'flex',
307
+ alignItems: 'center',
308
+ gap: '0.75rem',
309
+ margin: '1rem',
310
+ justifyContent: 'center',
311
+ }}
312
+ >
313
+ <div className={styles.interventionSection}>
314
+ <span>Intervention name</span>
315
+ <Tag type="blue">{claimInterventions.intervention_name}</Tag>
316
+ </div>
317
+ <div className={styles.interventionSection}>
318
+ <span>Intervention code</span>
319
+ <Tag type="blue">{claimInterventions.intervention_code}</Tag>
320
+ </div>
288
321
  </div>
289
322
  </div>
290
- </div>
291
- <div className={styles.fileList}>
292
- {documents.map((document) => (
293
- <div key={document.id} className={styles.fileItem}>
294
- <div
295
- style={{
296
- display: 'flex',
297
- alignItems: 'center',
298
- gap: '0.75rem',
299
- flex: 1,
300
- }}
301
- >
302
- <DocumentPdf size={20} />
303
- <span>{document.name}</span>
304
- </div>
305
-
306
- {!document.generated ? (
307
- <Button
308
- kind="ghost"
309
- size="sm"
310
- onClick={() => generateDocument(document)}
311
- disabled={generatingDocumentId !== null}
312
- >
313
- {generatingDocumentId === document.id ? <InlineLoading description="Generating…" /> : 'Generate'}
314
- </Button>
315
- ) : (
323
+ <div className={styles.fileList}>
324
+ {documents.map((document) => (
325
+ <div key={document.id} className={styles.fileItem}>
316
326
  <div
317
327
  style={{
318
328
  display: 'flex',
319
329
  alignItems: 'center',
320
- gap: '0.5rem',
330
+ gap: '0.75rem',
331
+ flex: 1,
321
332
  }}
322
333
  >
334
+ <DocumentPdf size={20} />
335
+ <span>{document.name}</span>
336
+ </div>
337
+
338
+ {!document.generated ? (
323
339
  <Button
324
340
  kind="ghost"
325
341
  size="sm"
326
- renderIcon={View}
327
- onClick={() => {
328
- setPreviewUrl(document.url);
329
- setPreviewOpen(true);
330
- }}
342
+ onClick={() => generateDocument(document)}
343
+ disabled={
344
+ generatingDocumentId !== null ||
345
+ (document.name === ApplicableDocumentType.DEATH_NOTICE &&
346
+ (isLoadingDeathNotification || !deathNotificationPatient || !deathNotificationEncounter))
347
+ }
331
348
  >
332
- Preview
349
+ {isLoadingDeathNotification && document.name === ApplicableDocumentType.DEATH_NOTICE ? (
350
+ <InlineLoading description="Loading form…" />
351
+ ) : generatingDocumentId === document.id ? (
352
+ <InlineLoading description="Generating…" />
353
+ ) : (
354
+ 'Generate'
355
+ )}
333
356
  </Button>
334
-
335
- {!document.uploaded && (
336
- <Button kind="primary" size="sm" onClick={() => handleSubmit(document)}>
337
- Upload
357
+ ) : (
358
+ <div
359
+ style={{
360
+ display: 'flex',
361
+ alignItems: 'center',
362
+ gap: '0.5rem',
363
+ }}
364
+ >
365
+ <Button
366
+ kind="ghost"
367
+ size="sm"
368
+ renderIcon={View}
369
+ onClick={() => {
370
+ setPreviewUrl(document.url);
371
+ setPreviewOpen(true);
372
+ }}
373
+ >
374
+ Preview
338
375
  </Button>
339
- )}
340
376
 
341
- <Button kind="ghost" size="sm" renderIcon={TrashCan} onClick={() => deleteDocument(document.id)}>
342
- Delete
343
- </Button>
344
- </div>
345
- )}
346
- </div>
347
- ))}
348
- </div>
349
- <div className={styles.closeButton}>
350
- <Button kind="secondary" onClick={handleDiscard}>
351
- {t('close', 'Close')}
352
- </Button>
377
+ {!document.uploaded && (
378
+ <Button kind="primary" size="sm" onClick={() => handleSubmit(document)}>
379
+ Upload
380
+ </Button>
381
+ )}
382
+
383
+ <Button kind="ghost" size="sm" renderIcon={TrashCan} onClick={() => deleteDocument(document.id)}>
384
+ Delete
385
+ </Button>
386
+ </div>
387
+ )}
388
+ </div>
389
+ ))}
390
+ </div>
391
+ <div className={styles.closeButton}>
392
+ <Button kind="secondary" onClick={handleDiscard}>
393
+ {t('close', 'Close')}
394
+ </Button>
395
+ </div>
396
+ </Form>
397
+ <Modal
398
+ open={previewOpen}
399
+ passiveModal
400
+ modalHeading="Document Preview"
401
+ onRequestClose={() => setPreviewOpen(false)}
402
+ size="lg"
403
+ >
404
+ {previewUrl && (
405
+ <iframe
406
+ src={previewUrl}
407
+ title="Preview"
408
+ style={{
409
+ width: '100%',
410
+ height: '80vh',
411
+ border: 'none',
412
+ }}
413
+ />
414
+ )}
415
+ </Modal>
416
+ <div
417
+ style={{
418
+ position: 'fixed',
419
+ left: '-9999px',
420
+ top: 0,
421
+ opacity: 0,
422
+ pointerEvents: 'none',
423
+ zIndex: -1,
424
+ }}
425
+ >
426
+ <InvoiceComponent ref={invoiceRef} bill={bill} patientUuid={patientUuid} />
427
+
428
+ <DischargeSummaryComponent ref={dischargeRef} claimIntervention={claimInterventions} bill={bill} />
429
+
430
+ <FinalBillComponent ref={finalBillRef} bill={bill} />
431
+ <CaseSummary ref={caseSummaryRef} patientUuid={patientUuid} billingDate={billingDate} />
432
+ <DialysisChart ref={dialysisRef} patientUuid={patientUuid} />
433
+ <GeneralDischargeSummary ref={generalDischargeSummary} patientUuid={patientUuid} />
434
+ <LabOrdersComponent ref={labResultsRef} patientUuid={patientUuid} billingDate={billingDate} />
435
+ <ProformaInvoiceComponent ref={proformaInvoiceRef} patientUuid={patientUuid} />
436
+ <UltrasoundReport ref={ultrasoundReportRef} patientUuid={patientUuid} />
437
+ {deathNotificationPatient && deathNotificationEncounter && (
438
+ <DeathNotification
439
+ ref={deathNotificationRef}
440
+ patient={deathNotificationPatient}
441
+ encounter={deathNotificationEncounter}
442
+ />
443
+ )}
353
444
  </div>
354
- </Form>
355
- <Modal
356
- open={previewOpen}
357
- passiveModal
358
- modalHeading="Document Preview"
359
- onRequestClose={() => setPreviewOpen(false)}
360
- size="lg"
361
- >
362
- {previewUrl && (
363
- <iframe
364
- src={previewUrl}
365
- title="Preview"
366
- style={{
367
- width: '100%',
368
- height: '80vh',
369
- border: 'none',
370
- }}
371
- />
372
- )}
373
- </Modal>
374
- <div
375
- style={{
376
- position: 'fixed',
377
- left: '-9999px',
378
- top: 0,
379
- opacity: 0,
380
- pointerEvents: 'none',
381
- zIndex: -1,
382
- }}
383
- >
384
- <InvoiceComponent ref={invoiceRef} bill={bill} patientUuid={patientUuid} />
385
-
386
- <DischargeSummaryComponent ref={dischargeRef} claimIntervention={claimInterventions} bill={bill} />
387
-
388
- <FinalBillComponent ref={finalBillRef} bill={bill} />
389
- <CaseSummary ref={caseSummaryRef} patientUuid={patientUuid} billingDate={billingDate} />
390
- <DialysisChart ref={dialysisRef} patientUuid={patientUuid} />
391
- <GeneralDischargeSummary ref={generalDischargeSummary} patientUuid={patientUuid} />
392
- <LabOrdersComponent ref={labResultsRef} patientUuid={patientUuid} billingDate={billingDate} />
393
- <ProformaInvoiceComponent ref={proformaInvoiceRef} patientUuid={patientUuid} />
394
- <UltrasoundReport ref={ultrasoundReportRef} patientUuid={patientUuid} />
395
- </div>
396
445
  </>
397
446
  </Workspace2>
398
447
  );
@@ -125,6 +125,7 @@ export type ClaimVisitInvoince = {
125
125
  export enum ApplicableDocumentType {
126
126
  BIRTH_NOTIFICATION = 'BIRTH_NOTIFICATION',
127
127
  CLAIM_FORM = 'CLAIM_FORM',
128
+ DEATH_NOTICE = 'DEATH_NOTICE',
128
129
  DISCHARGE_SUMMARY = 'DISCHARGE_SUMMARY',
129
130
  INVOICE = 'INVOICE',
130
131
  FINAL_BILL = 'FINAL_BILL',
@@ -31,6 +31,7 @@ interface claimInterventionDetailsProps {
31
31
  canSwitchIntervention?: boolean;
32
32
  onSwitchSuccess?: () => void;
33
33
  billingDate: string;
34
+ hasDeathReportingFormEncounter?: boolean;
34
35
  }
35
36
 
36
37
  const isActiveIntervention = (iv: VisitIntervention) => (iv.workflow_state ?? '').toUpperCase() === 'ACTIVE';
@@ -44,6 +45,7 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
44
45
  canSwitchIntervention = false,
45
46
  onSwitchSuccess,
46
47
  billingDate,
48
+ hasDeathReportingFormEncounter
47
49
  }) => {
48
50
  const session = useSession();
49
51
  const locationUuid = session?.sessionLocation?.uuid;
@@ -211,7 +213,7 @@ const ClaimInterventionDetails: React.FC<claimInterventionDetailsProps> = ({
211
213
  claimInterventions.map((ci) => {
212
214
  const canSwitch = canSwitchFor(ci);
213
215
  const canRaise = canRaisePreauthFor(ci);
214
- const hasAttachments = (ci.applicable_document_types ?? []).length > 0;
216
+ const hasAttachments = (ci.applicable_document_types ?? []).length > 0 || hasDeathReportingFormEncounter;
215
217
  return (
216
218
  <TableRow key={ci.id}>
217
219
  <TableCell>{ci.intervention_code}</TableCell>
@@ -328,6 +328,7 @@ const ClaimVisitDetails: React.FC<claimVisitDetailsProps> = ({
328
328
  onBillDetailsChange?.();
329
329
  }}
330
330
  billingDate={billingDate}
331
+ hasDeathReportingFormEncounter={hasDeathReportingFormEncounter}
331
332
  />
332
333
  )}
333
334
  </div>
@@ -1,7 +1,12 @@
1
- import React, { useRef, useState } from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
 
3
- import { showSnackbar, useSession, type DefaultWorkspaceProps } from '@openmrs/esm-framework';
3
+ import {
4
+ showSnackbar,
5
+ useSession,
6
+ type DefaultWorkspaceProps,
7
+ } from '@openmrs/esm-framework';
4
8
  import { ApplicableDocumentType, type VisitIntervention } from '../../types';
9
+ import { useDeathNotification } from '../../../v2/patient-bill-details/attachments/death-notification.resource';
5
10
 
6
11
  import styles from './attachments.scss';
7
12
  import { Button, Form, InlineLoading, Modal, Tag } from '@carbon/react';
@@ -20,6 +25,7 @@ import GeneralDischargeSummary from './general-discharge-summary/general-dischar
20
25
  import LabOrdersComponent from './lab-results/lab-orders.component';
21
26
  import ProformaInvoiceComponent from './proforma-invoice/proforma-invoice.component';
22
27
  import UltrasoundReport from './ultrasound-report/utlrasound-report.component';
28
+ import DeathNotification from '../../../v2/patient-bill-details/attachments/death-notification.component';
23
29
 
24
30
  interface GenerateAttachmentsProps extends DefaultWorkspaceProps {
25
31
  claimInterventions: VisitIntervention;
@@ -39,6 +45,11 @@ const GenerateAttachments: React.FC<GenerateAttachmentsProps> = ({
39
45
  billingDate,
40
46
  }) => {
41
47
  const { t } = useTranslation();
48
+ const {
49
+ patient: deathNotificationPatient,
50
+ encounter: deathNotificationEncounter,
51
+ isLoading: isLoadingDeathNotification,
52
+ } = useDeathNotification(patientUuid, true);
42
53
  const [previewUrl, setPreviewUrl] = useState<string>();
43
54
  const [previewOpen, setPreviewOpen] = useState(false);
44
55
  const [generatingDocumentId, setGeneratingDocumentId] = useState<string | null>(null);
@@ -75,6 +86,27 @@ const GenerateAttachments: React.FC<GenerateAttachmentsProps> = ({
75
86
  const labResultsRef = useRef<HTMLDivElement>(null);
76
87
  const proformaInvoiceRef = useRef<HTMLDivElement>(null);
77
88
  const ultrasoundReportRef = useRef<HTMLDivElement>(null);
89
+ const deathNotificationRef = useRef<HTMLDivElement>(null);
90
+
91
+ useEffect(() => {
92
+ if (!deathNotificationEncounter) {
93
+ return;
94
+ }
95
+
96
+ setDocuments((previous) =>
97
+ previous.some((document) => document.name.trim().toUpperCase() === ApplicableDocumentType.DEATH_NOTICE)
98
+ ? previous
99
+ : [
100
+ ...previous,
101
+ {
102
+ id: crypto.randomUUID(),
103
+ name: ApplicableDocumentType.DEATH_NOTICE,
104
+ generated: false,
105
+ uploaded: false,
106
+ },
107
+ ],
108
+ );
109
+ }, [deathNotificationEncounter]);
78
110
 
79
111
  if (!claimInterventions) return null;
80
112
 
@@ -222,6 +254,10 @@ const GenerateAttachments: React.FC<GenerateAttachmentsProps> = ({
222
254
  element = ultrasoundReportRef.current;
223
255
  break;
224
256
 
257
+ case ApplicableDocumentType.DEATH_NOTICE:
258
+ element = deathNotificationRef.current;
259
+ break;
260
+
225
261
  default:
226
262
  console.warn(`${document.name} cannot be generated. Kindly upload it instead using the upload button.`);
227
263
  showSnackbar({
@@ -305,9 +341,19 @@ const GenerateAttachments: React.FC<GenerateAttachmentsProps> = ({
305
341
  kind="ghost"
306
342
  size="sm"
307
343
  onClick={() => generateDocument(document)}
308
- disabled={generatingDocumentId !== null}
344
+ disabled={
345
+ generatingDocumentId !== null ||
346
+ (document.name === ApplicableDocumentType.DEATH_NOTICE &&
347
+ (isLoadingDeathNotification || !deathNotificationPatient || !deathNotificationEncounter))
348
+ }
309
349
  >
310
- {generatingDocumentId === document.id ? <InlineLoading description="Generating…" /> : 'Generate'}
350
+ {isLoadingDeathNotification && document.name === ApplicableDocumentType.DEATH_NOTICE ? (
351
+ <InlineLoading description="Loading form…" />
352
+ ) : generatingDocumentId === document.id ? (
353
+ <InlineLoading description="Generating…" />
354
+ ) : (
355
+ 'Generate'
356
+ )}
311
357
  </Button>
312
358
  ) : (
313
359
  <div
@@ -389,6 +435,13 @@ const GenerateAttachments: React.FC<GenerateAttachmentsProps> = ({
389
435
  <LabOrdersComponent ref={labResultsRef} patientUuid={patientUuid} billingDate={billingDate} />
390
436
  <ProformaInvoiceComponent ref={proformaInvoiceRef} patientUuid={patientUuid} />
391
437
  <UltrasoundReport ref={ultrasoundReportRef} patientUuid={patientUuid} />
438
+ {deathNotificationPatient && deathNotificationEncounter && (
439
+ <DeathNotification
440
+ ref={deathNotificationRef}
441
+ patient={deathNotificationPatient}
442
+ encounter={deathNotificationEncounter}
443
+ />
444
+ )}
392
445
  </div>
393
446
  </>
394
447
  );
@@ -103,6 +103,7 @@ export type ClaimVisitInvoince = {
103
103
  export enum ApplicableDocumentType {
104
104
  BIRTH_NOTIFICATION = 'BIRTH_NOTIFICATION',
105
105
  CLAIM_FORM = 'CLAIM_FORM',
106
+ DEATH_NOTICE = 'DEATH_NOTICE',
106
107
  DISCHARGE_SUMMARY = 'DISCHARGE_SUMMARY',
107
108
  INVOICE = 'INVOICE',
108
109
  FINAL_BILL = 'FINAL_BILL',