@ampath/esm-dha-workflow-app 4.0.0-next.13 → 4.0.0-next.131
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/dist/1197.js +1 -0
- package/dist/1277.js +1 -0
- package/dist/1277.js.map +1 -0
- package/dist/1558.js +2 -0
- package/dist/{198.js.LICENSE.txt → 1558.js.LICENSE.txt} +0 -10
- package/dist/1558.js.map +1 -0
- package/dist/1729.js +1 -0
- package/dist/1729.js.map +1 -0
- package/dist/182.js +1 -0
- package/dist/182.js.map +1 -0
- package/dist/1826.js +1 -0
- package/dist/1826.js.map +1 -0
- package/dist/1833.js +1 -0
- package/dist/1833.js.map +1 -0
- package/dist/1856.js +1 -0
- package/dist/1856.js.map +1 -0
- package/dist/1925.js +1 -0
- package/dist/1925.js.map +1 -0
- package/dist/2021.js +1 -0
- package/dist/2021.js.map +1 -0
- package/dist/2054.js +1 -0
- package/dist/2054.js.map +1 -0
- package/dist/2177.js +2 -0
- package/dist/2177.js.LICENSE.txt +9 -0
- package/dist/2177.js.map +1 -0
- package/dist/2364.js +2 -0
- package/dist/{200.js.LICENSE.txt → 2364.js.LICENSE.txt} +15 -0
- package/dist/2364.js.map +1 -0
- package/dist/2566.js +1 -0
- package/dist/2566.js.map +1 -0
- package/dist/2801.js +1 -0
- package/dist/2801.js.map +1 -0
- package/dist/2975.js +1 -0
- package/dist/2975.js.map +1 -0
- package/dist/3041.js +1 -0
- package/dist/3041.js.map +1 -0
- package/dist/3099.js +1 -0
- package/dist/3184.js +2 -0
- package/dist/3184.js.map +1 -0
- package/dist/3485.js +1 -0
- package/dist/3485.js.map +1 -0
- package/dist/3654.js +1 -0
- package/dist/3654.js.map +1 -0
- package/dist/4055.js +1 -0
- package/dist/4205.js +1 -0
- package/dist/4205.js.map +1 -0
- package/dist/4225.js +1 -0
- package/dist/4225.js.map +1 -0
- package/dist/4300.js +1 -0
- package/dist/4335.js +1 -0
- package/dist/4353.js +1 -0
- package/dist/4353.js.map +1 -0
- package/dist/4517.js +1 -0
- package/dist/4517.js.map +1 -0
- package/dist/4652.js +1 -0
- package/dist/4674.js +1 -0
- package/dist/4674.js.map +1 -0
- package/dist/4947.js +1 -0
- package/dist/4947.js.map +1 -0
- package/dist/5015.js +1 -0
- package/dist/5015.js.map +1 -0
- package/dist/5422.js +1 -0
- package/dist/5422.js.map +1 -0
- package/dist/5428.js +1 -0
- package/dist/5428.js.map +1 -0
- package/dist/5752.js +1 -0
- package/dist/5752.js.map +1 -0
- package/dist/5851.js +1 -0
- package/dist/5851.js.map +1 -0
- package/dist/6264.js +1 -0
- package/dist/6264.js.map +1 -0
- package/dist/6540.js +2 -0
- package/dist/6540.js.map +1 -0
- package/dist/6606.js +1 -0
- package/dist/6606.js.map +1 -0
- package/dist/693.js +1 -0
- package/dist/693.js.map +1 -0
- package/dist/6991.js +1 -0
- package/dist/6991.js.map +1 -0
- package/dist/7199.js +1 -0
- package/dist/7199.js.map +1 -0
- package/dist/7255.js +1 -0
- package/dist/7255.js.map +1 -0
- package/dist/7258.js +1 -0
- package/dist/7258.js.map +1 -0
- package/dist/7420.js +1 -0
- package/dist/7420.js.map +1 -0
- package/dist/8114.js +1 -0
- package/dist/8114.js.map +1 -0
- package/dist/8339.js +1 -0
- package/dist/8339.js.map +1 -0
- package/dist/8865.js +1 -0
- package/dist/8865.js.map +1 -0
- package/dist/8895.js +1 -0
- package/dist/8895.js.map +1 -0
- package/dist/9037.js +1 -0
- package/dist/9037.js.map +1 -0
- package/dist/9091.js +1 -0
- package/dist/9091.js.map +1 -0
- package/dist/9093.js +1 -0
- package/dist/9093.js.map +1 -0
- package/dist/9117.js +1 -0
- package/dist/9117.js.map +1 -0
- package/dist/9721.js +1 -0
- package/dist/9721.js.map +1 -0
- package/dist/esm-dha-workflow-app.js +1 -0
- package/dist/esm-dha-workflow-app.js.buildmanifest.json +1546 -0
- package/dist/esm-dha-workflow-app.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +11 -4
- package/src/accounting/accounting.component.tsx +13 -0
- package/src/admissions/admission-request-list/admission-request-list.scss +0 -0
- package/src/admissions/admission-request-list/admission-request-list.tsx +134 -0
- package/src/admissions/admissions-dashboard.component.scss +15 -0
- package/src/admissions/admissions-dashboard.component.tsx +160 -0
- package/src/admissions/admissions.resource.ts +101 -0
- package/src/admissions/admitted-list/admitted-patients-list.tsx +141 -0
- package/src/admissions/constants/index.ts +33 -0
- package/src/admissions/discharged-list/discharged-list.scss +0 -0
- package/src/admissions/discharged-list/discharged-list.tsx +58 -0
- package/src/admissions/inpatient/admission-history/patient-admission-history.tsx +76 -0
- package/src/admissions/inpatient/admission-requests/patient-admission-requests.scss +19 -0
- package/src/admissions/inpatient/admission-requests/patient-admission-requests.tsx +72 -0
- package/src/admissions/inpatient/inpatient-admissions.component.scss +7 -0
- package/src/admissions/inpatient/inpatient-admissions.component.tsx +46 -0
- package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.scss +23 -0
- package/src/admissions/modal/admit-elsewhere/admit-elsewhere.modal.tsx +156 -0
- package/src/admissions/modal/admit-patient/admit-patient.modal.scss +0 -0
- package/src/admissions/modal/admit-patient/admit-patient.modal.tsx +109 -0
- package/src/admissions/modal/bed-swap/bed-swap.modal.scss +0 -0
- package/src/admissions/modal/bed-swap/bed-swap.modal.tsx +102 -0
- package/src/admissions/modal/cancel-admission-request/cancel-admission-request.scss +11 -0
- package/src/admissions/modal/cancel-admission-request/cancel-admission-request.tsx +105 -0
- package/src/admissions/modal/discharge/discharge-patient.modal.scss +13 -0
- package/src/admissions/modal/discharge/discharge-patient.modal.tsx +84 -0
- package/src/admissions/types/index.ts +192 -0
- package/src/appointments/appointments.component.tsx +13 -0
- package/src/billing/api/billing.api.ts +95 -0
- package/src/billing/billing-root.tsx +20 -0
- package/src/billing/dashboard/billingDashboard.component.scss +0 -0
- package/src/billing/dashboard/billingDashboard.component.tsx +25 -0
- package/src/billing/extensions/visit-billing/visit-billing.extension.scss +0 -0
- package/src/billing/extensions/visit-billing/visit-billing.extension.tsx +409 -0
- package/src/billing/invoice/bill.resource.ts +47 -0
- package/src/billing/invoice/invoice-header/header-card/header-card.scss +17 -0
- package/src/billing/invoice/invoice-header/header-card/header-card.tsx +21 -0
- package/src/billing/invoice/invoice.scss +113 -0
- package/src/billing/invoice/invoice.tsx +1371 -0
- package/src/billing/invoice/line-items/line-items.scss +0 -0
- package/src/billing/invoice/line-items/line-items.tsx +120 -0
- package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.scss +23 -0
- package/src/billing/invoice/modal/delete-bill-item/delete-bill-item.modal.tsx +114 -0
- package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.scss +0 -0
- package/src/billing/invoice/modal/edit-bill-item/edit-bill-item.modal.tsx +122 -0
- package/src/billing/invoice/payment-details/payment-details.scss +0 -0
- package/src/billing/invoice/payment-details/payment-details.tsx +43 -0
- package/src/billing/invoice/print-invoice/print-receipt.component.tsx +44 -0
- package/src/billing/invoice/print-invoice/print-receipt.scss +14 -0
- package/src/billing/types/index.ts +84 -0
- package/src/billing/utils/index.ts +17 -0
- package/src/billing/widgets/billingTotalsRow.component.scss +0 -0
- package/src/billing/widgets/billingTotalsRow.component.tsx +712 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.resource.ts +85 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.scss +58 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/create-order-bill-form.workspace.tsx +330 -0
- package/src/billing/workspaces/create-order-bill-form-workspace/schema.ts +11 -0
- package/src/bookings/bookings.component.scss +7 -0
- package/src/bookings/bookings.component.tsx +31 -0
- package/src/bookings/daily/daily-bookings.component.scss +38 -0
- package/src/bookings/daily/daily-bookings.component.tsx +138 -0
- package/src/bookings/daily/daily-bookings.resource.ts +28 -0
- package/src/bookings/daily/filters/daily-bookings-filter.component.scss +15 -0
- package/src/bookings/daily/filters/daily-bookings-filter.component.tsx +80 -0
- package/src/bookings/daily/patient-list/daily-bookings-patient-list.component.tsx +97 -0
- package/src/bookings/types/index.ts +68 -0
- package/src/config-schema.ts +186 -32
- package/src/createDashboardLink.tsx +9 -0
- package/src/dashboard/charts/chart.component.scss +13 -0
- package/src/dashboard/charts/chart.component.tsx +54 -0
- package/src/dashboard/dashboard.component.scss +7 -0
- package/src/dashboard/dashboard.component.tsx +51 -0
- package/src/dashboard/overview/overview.component.scss +147 -0
- package/src/dashboard/overview/overview.component.tsx +115 -0
- package/src/dashboard/patient-list/patient-list.component.tsx +41 -0
- package/src/dashboard-meta/accounting-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/admissions-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/billing-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/bookings-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/consultation-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/dha-workflow-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/inpatient-admissions.meta.ts +6 -0
- package/src/dashboard-meta/mch-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/pharmacy-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/queue-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/registers-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/registry-dashboard.meta.ts +6 -0
- package/src/dashboard-meta/service-queue-admin.meta.ts +6 -0
- package/src/dashboard-meta/triage-dashboard.meta.ts +6 -0
- package/src/hooks/useActions.ts +165 -0
- package/src/index.ts +113 -1
- package/src/laboratory/laboratory.component.tsx +13 -0
- package/src/left-panel/left-panel.component.tsx +0 -2
- package/src/mch/mch-queues.component.scss +7 -0
- package/src/mch/mch-queues.component.tsx +32 -0
- package/src/mch/queues/consultation/mch-consultation.tsx +18 -0
- package/src/mch/queues/triage/mch-triage.tsx +15 -0
- package/src/modals/sign-off-modal.scss +7 -0
- package/src/modals/sign-off-modal.tsx +52 -0
- package/src/mortuary/mortuary.component.tsx +13 -0
- package/src/pharmacy/pharmacy.component.tsx +13 -0
- package/src/registers/registers.component.tsx +8 -0
- package/src/registry/eligibility/eliigibility-tags/eligibility-tags.scss +6 -0
- package/src/registry/eligibility/eliigibility-tags/eligibility-tags.tsx +137 -0
- package/src/registry/eligibility/modal/eligibility-details.modal.scss +38 -0
- package/src/registry/eligibility/modal/eligibility-details.modal.tsx +131 -0
- package/src/registry/modal/client-details-modal/client-details-modal.tsx +9 -26
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.scss +31 -1
- package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +145 -58
- package/src/registry/modal/send-to-triage/send-to-triage.modal.scss +16 -1
- package/src/registry/modal/send-to-triage/send-to-triage.modal.tsx +681 -97
- package/src/registry/payment-details/payment-options/payment-options.scss +13 -0
- package/src/registry/payment-details/payment-options/payment-options.tsx +61 -14
- package/src/registry/registry.component.scss +18 -1
- package/src/registry/registry.component.tsx +242 -111
- package/src/registry/registry.resource.ts +8 -5
- package/src/registry/types/index.ts +179 -1
- package/src/registry/utils/error-handler.ts +33 -0
- package/src/registry/utils/format-dependant-display-data.ts +8 -0
- package/src/registry/utils/hie-client-adapter.ts +312 -0
- package/src/registry/utils/mask-data.ts +11 -0
- package/src/resources/dashboard.resource.ts +20 -0
- package/src/resources/hie-amrs-automatic-registration.service.ts +16 -0
- package/src/resources/identifier-types.ts +29 -0
- package/src/resources/patient-resource.ts +62 -0
- package/src/resources/queue.resource.ts +23 -0
- package/src/resources/superset.resource.ts +20 -0
- package/src/resources/visit.resource.ts +19 -1
- package/src/root.component.tsx +32 -6
- package/src/routes.json +215 -7
- package/src/service-queues/action-button.component.tsx +34 -0
- package/src/service-queues/action-overflow-menu-item.component.tsx +34 -0
- package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.scss +31 -0
- package/src/service-queues/admin/queue/modal/create-queue/create-queue.modal.tsx +142 -0
- package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.scss +0 -0
- package/src/service-queues/admin/queue/modal/edit-queue/edit-queue.modal.tsx +150 -0
- package/src/service-queues/admin/queue/queue-list/queue-list.scss +33 -0
- package/src/service-queues/admin/queue/queue-list/queue-list.tsx +113 -0
- package/src/service-queues/admin/queue-entry/queue-entry-list.component.scss +6 -0
- package/src/service-queues/admin/queue-entry/queue-entry-list.component.tsx +137 -0
- package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.scss +31 -0
- package/src/service-queues/admin/queue-room/modal/create-queue-room/create-queue-room.modal.tsx +137 -0
- package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.scss +0 -0
- package/src/service-queues/admin/queue-room/modal/edit-queue-room/edit-queue-room.modal.tsx +146 -0
- package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.scss +22 -0
- package/src/service-queues/admin/queue-room/queue-room-list/queue-room-list.tsx +120 -0
- package/src/service-queues/admin/service-queue-admin-dashboard.component.scss +6 -0
- package/src/service-queues/admin/service-queue-admin-dashboard.component.tsx +34 -0
- package/src/service-queues/consultation/consultation.component.scss +7 -0
- package/src/service-queues/consultation/consultation.component.tsx +15 -0
- package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.scss +30 -0
- package/src/service-queues/extensions/service-queue-patient-banner/service-queue-patient-banner.tsx +108 -0
- package/src/service-queues/metrics/metrics-cards/attended-patients.extension.tsx +38 -0
- package/src/service-queues/metrics/metrics-cards/metrics-card.component.tsx +86 -0
- package/src/service-queues/metrics/metrics-cards/metrics-card.scss +106 -0
- package/src/service-queues/metrics/metrics-cards/waiting-patients.extension.tsx +34 -0
- package/src/service-queues/metrics/metrics-container.component.tsx +23 -0
- package/src/service-queues/metrics/metrics-container.scss +36 -0
- package/src/service-queues/metrics/metrics.resource.ts +65 -0
- package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.scss +26 -0
- package/src/service-queues/modals/daily-report-patient-list/daily-report-patient-list.tsx +108 -0
- package/src/service-queues/modals/move/move-patient.component.scss +35 -0
- package/src/service-queues/modals/move/move-patient.component.tsx +152 -0
- package/src/service-queues/modals/serve/serve-patient.comppnent.scss +0 -0
- package/src/service-queues/modals/serve/serve-patient.comppnent.tsx +80 -0
- package/src/service-queues/modals/sign-off/sign-off.modal.scss +0 -0
- package/src/service-queues/modals/sign-off/sign-off.modal.tsx +79 -0
- package/src/service-queues/modals/transition/transition-patient.component.scss +0 -0
- package/src/service-queues/modals/transition/transition-patient.component.tsx +122 -0
- package/src/service-queues/queue-list/check-in.service.ts +26 -0
- package/src/service-queues/queue-list/queue-list.component.scss +36 -0
- package/src/service-queues/queue-list/queue-list.component.tsx +249 -0
- package/src/service-queues/queue-room.component.tsx +39 -0
- package/src/service-queues/room/room.component.tsx +58 -0
- package/src/service-queues/service-queue/service-queue.component.scss +14 -0
- package/src/service-queues/service-queue/service-queue.component.tsx +399 -0
- package/src/service-queues/service-queue/stats/stat-details/stat-details.component.scss +12 -0
- package/src/service-queues/service-queue/stats/stat-details/stat-details.component.tsx +63 -0
- package/src/service-queues/service-queue.scss +27 -0
- package/src/service-queues/service-queue.tsx +37 -0
- package/src/service-queues/service-queues.resource.ts +361 -0
- package/src/service-queues/service.resource.ts +28 -0
- package/src/shared/constants/civil-status.ts +29 -0
- package/src/shared/constants/concepts.ts +15 -0
- package/src/shared/constants/index.ts +1 -2
- package/src/shared/constants/patient-category.ts +6 -0
- package/src/shared/constants/person-attributes.ts +33 -0
- package/src/shared/constants/visit-types.ts +7 -0
- package/src/shared/services/billing.resource.ts +38 -0
- package/src/shared/services/client-payment-mode.resource.ts +27 -0
- package/src/shared/services/client-payment.resource.ts +17 -0
- package/src/shared/services/eligibility.resource.ts +19 -0
- package/src/shared/services/encounters.resource.ts +27 -0
- package/src/shared/services/location.resource.ts +9 -0
- package/src/shared/types/index.ts +152 -0
- package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.scss +42 -0
- package/src/shared/ui/aggregate-stat-card/aggregate-stat-card.tsx +42 -0
- package/src/shared/ui/confirm-modal/confirm.modal.scss +18 -0
- package/src/shared/ui/confirm-modal/confirm.modal.tsx +38 -0
- package/src/shared/ui/otp-input/otp-input.component.scss +14 -0
- package/src/shared/ui/otp-input/otp-input.component.tsx +90 -0
- package/src/shared/ui/stat-card/stat-card.component.scss +10 -0
- package/src/shared/ui/stat-card/stat-card.component.tsx +26 -0
- package/src/shared/ui/timer/timer.component.scss +5 -0
- package/src/shared/ui/timer/timer.component.tsx +40 -0
- package/src/shared/utils/get-base-url.ts +27 -0
- package/src/shared/utils/get-tag-type.ts +66 -0
- package/src/side-nav-menu/nav-link-config.ts +6 -4
- package/src/triage/metrics/attended-patients.extension.tsx +42 -0
- package/src/triage/metrics/metrics.scss +36 -0
- package/src/triage/metrics/triage-metrics.component.tsx +21 -0
- package/src/triage/metrics/waiting-patients.extension.tsx +39 -0
- package/src/triage/room/room.scss +29 -0
- package/src/triage/triage.component.tsx +15 -0
- package/src/triage/triage.resource.ts +19 -0
- package/src/triage/types.ts +16 -0
- package/src/types/types.ts +180 -0
- package/dist/16.js +0 -1
- package/dist/16.js.map +0 -1
- package/dist/184.js +0 -2
- package/dist/184.js.map +0 -1
- package/dist/197.js +0 -1
- package/dist/198.js +0 -2
- package/dist/198.js.map +0 -1
- package/dist/200.js +0 -2
- package/dist/200.js.map +0 -1
- package/dist/255.js +0 -1
- package/dist/255.js.map +0 -1
- package/dist/300.js +0 -1
- package/dist/335.js +0 -1
- package/dist/353.js +0 -1
- package/dist/353.js.map +0 -1
- package/dist/420.js +0 -1
- package/dist/420.js.map +0 -1
- package/dist/540.js +0 -2
- package/dist/540.js.map +0 -1
- package/dist/55.js +0 -1
- package/dist/652.js +0 -1
- package/dist/860.js +0 -1
- package/dist/860.js.map +0 -1
- package/dist/91.js +0 -1
- package/dist/91.js.map +0 -1
- package/dist/93.js +0 -1
- package/dist/93.js.map +0 -1
- package/dist/99.js +0 -1
- package/dist/openmrs-esm-home-app.js +0 -1
- package/dist/openmrs-esm-home-app.js.buildmanifest.json +0 -626
- package/dist/openmrs-esm-home-app.js.map +0 -1
- package/src/consultation/consultation.tsx +0 -7
- /package/dist/{184.js.LICENSE.txt → 3184.js.LICENSE.txt} +0 -0
- /package/dist/{540.js.LICENSE.txt → 6540.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,1371 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Grid,
|
|
4
|
+
Column,
|
|
5
|
+
Tile,
|
|
6
|
+
DataTable,
|
|
7
|
+
Table,
|
|
8
|
+
TableHead,
|
|
9
|
+
TableRow,
|
|
10
|
+
TableHeader,
|
|
11
|
+
TableBody,
|
|
12
|
+
TableCell,
|
|
13
|
+
Breadcrumb,
|
|
14
|
+
BreadcrumbItem,
|
|
15
|
+
Button,
|
|
16
|
+
Modal,
|
|
17
|
+
TextInput,
|
|
18
|
+
NumberInput,
|
|
19
|
+
Tag,
|
|
20
|
+
Stack,
|
|
21
|
+
Checkbox,
|
|
22
|
+
OverflowMenu,
|
|
23
|
+
OverflowMenuItem,
|
|
24
|
+
SkeletonText,
|
|
25
|
+
SkeletonPlaceholder,
|
|
26
|
+
} from '@carbon/react';
|
|
27
|
+
import { navigate, showSnackbar } from '@openmrs/esm-framework';
|
|
28
|
+
|
|
29
|
+
import {
|
|
30
|
+
Receipt,
|
|
31
|
+
DocumentAdd,
|
|
32
|
+
Subtract,
|
|
33
|
+
Money,
|
|
34
|
+
View,
|
|
35
|
+
CheckmarkFilled,
|
|
36
|
+
WarningAlt,
|
|
37
|
+
PendingFilled,
|
|
38
|
+
ResultNew,
|
|
39
|
+
} from '@carbon/react/icons';
|
|
40
|
+
import {
|
|
41
|
+
fetchBillById,
|
|
42
|
+
processPayment,
|
|
43
|
+
fetchPaymentModes,
|
|
44
|
+
checkClaimStatus,
|
|
45
|
+
raiseSHAClaim,
|
|
46
|
+
UpdateBillItemStatus,
|
|
47
|
+
} from '../api/billing.api';
|
|
48
|
+
import EligibilityTags from '../../registry/eligibility/eliigibility-tags/eligibility-tags';
|
|
49
|
+
|
|
50
|
+
type LineItemStatus = 'PENDING' | 'PAID' | 'CLAIMED' | 'WAIVED';
|
|
51
|
+
|
|
52
|
+
type LineItem = {
|
|
53
|
+
id: string;
|
|
54
|
+
service: string;
|
|
55
|
+
dept: string;
|
|
56
|
+
price: number;
|
|
57
|
+
payerType: 'CASH' | 'SHA';
|
|
58
|
+
supportsSha: boolean;
|
|
59
|
+
quantity: number;
|
|
60
|
+
paidQuantity?: number;
|
|
61
|
+
waivedAmount?: number;
|
|
62
|
+
claimedAmount?: number;
|
|
63
|
+
waiverAllowed: boolean;
|
|
64
|
+
status: LineItemStatus;
|
|
65
|
+
paymentReference?: string;
|
|
66
|
+
selected?: boolean;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
type PaymentLog = {
|
|
70
|
+
id: string;
|
|
71
|
+
type: 'CASH' | 'WAIVER' | 'SHA';
|
|
72
|
+
items: LineItem[];
|
|
73
|
+
totalAmount: number;
|
|
74
|
+
reference?: string;
|
|
75
|
+
claimNumber?: string;
|
|
76
|
+
timestamp: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const headers = [
|
|
80
|
+
{ key: 'service', header: 'Bill Item' },
|
|
81
|
+
{ key: 'payerType', header: 'Payer' },
|
|
82
|
+
{ key: 'quantity', header: 'Quantity' },
|
|
83
|
+
{ key: 'price', header: 'Price' },
|
|
84
|
+
{ key: 'balance', header: 'Total' },
|
|
85
|
+
{ key: 'status', header: 'Status' },
|
|
86
|
+
{ key: 'select', header: 'Select' },
|
|
87
|
+
{ key: 'actions', header: 'Actions' },
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
const INSURANCE_MODES = ['PHC', 'SHIF', 'ECCIF', 'SHA'];
|
|
91
|
+
|
|
92
|
+
const formatBillDate = (dateString?: string) => {
|
|
93
|
+
if (!dateString) return '';
|
|
94
|
+
|
|
95
|
+
const date = new Date(dateString);
|
|
96
|
+
const now = new Date();
|
|
97
|
+
|
|
98
|
+
const isToday =
|
|
99
|
+
date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate();
|
|
100
|
+
|
|
101
|
+
const yesterday = new Date();
|
|
102
|
+
yesterday.setDate(now.getDate() - 1);
|
|
103
|
+
const isYesterday =
|
|
104
|
+
date.getFullYear() === yesterday.getFullYear() &&
|
|
105
|
+
date.getMonth() === yesterday.getMonth() &&
|
|
106
|
+
date.getDate() === yesterday.getDate();
|
|
107
|
+
|
|
108
|
+
const timeStr = date.toLocaleTimeString(undefined, { hour: 'numeric', minute: 'numeric', hour12: true });
|
|
109
|
+
|
|
110
|
+
if (isToday) return `Today, ${timeStr}`;
|
|
111
|
+
if (isYesterday) return `Yesterday, ${timeStr}`;
|
|
112
|
+
|
|
113
|
+
return date.toLocaleString(undefined, {
|
|
114
|
+
year: 'numeric',
|
|
115
|
+
month: 'short',
|
|
116
|
+
day: 'numeric',
|
|
117
|
+
hour: 'numeric',
|
|
118
|
+
minute: 'numeric',
|
|
119
|
+
hour12: true,
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const formatCurrency = (value: number) => `Ksh ${value.toLocaleString()}`;
|
|
124
|
+
|
|
125
|
+
// Add headers for transaction logs
|
|
126
|
+
const logHeaders = [
|
|
127
|
+
{ key: 'timestamp', header: 'Timestamp' },
|
|
128
|
+
{ key: 'type', header: 'Type' },
|
|
129
|
+
{ key: 'items', header: 'Items' },
|
|
130
|
+
{ key: 'totalAmount', header: 'Amount' },
|
|
131
|
+
{ key: 'reference', header: 'Reference / Claim #' },
|
|
132
|
+
];
|
|
133
|
+
|
|
134
|
+
const navigateToBillingPage = () => {
|
|
135
|
+
navigate({ to: `${window.spaBase}/home/billing`, templateParams: {} });
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const getBillIdFromUrl = () => {
|
|
139
|
+
const parts = window.location.pathname.split('/');
|
|
140
|
+
return parts[parts.length - 1];
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const BillDetails: React.FC = () => {
|
|
144
|
+
const [bill, setBill] = useState<any>(null);
|
|
145
|
+
const [items, setItems] = useState<LineItem[]>([]);
|
|
146
|
+
const [loading, setLoading] = useState(true);
|
|
147
|
+
|
|
148
|
+
const billId = getBillIdFromUrl();
|
|
149
|
+
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
async function loadBill() {
|
|
152
|
+
try {
|
|
153
|
+
const data = await fetchBillById(billId);
|
|
154
|
+
|
|
155
|
+
setBill(data);
|
|
156
|
+
|
|
157
|
+
const mappedItems: LineItem[] =
|
|
158
|
+
data?.lineItems?.map((li: any) => ({
|
|
159
|
+
id: li.uuid,
|
|
160
|
+
service: li.billableService || 'Service',
|
|
161
|
+
payerType: (li.priceName && li.priceName.toUpperCase()) || 'CASH',
|
|
162
|
+
supportsSha: li.supportsSha || false,
|
|
163
|
+
waiverAllowed: li.waiverAllowed || false,
|
|
164
|
+
quantity: li.quantity || 1,
|
|
165
|
+
price: li.price || 0,
|
|
166
|
+
status: (li.paymentStatus?.toUpperCase() as LineItemStatus) || 'PENDING',
|
|
167
|
+
})) || [];
|
|
168
|
+
|
|
169
|
+
setItems(mappedItems);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Failed to fetch bill', error);
|
|
172
|
+
} finally {
|
|
173
|
+
setLoading(false);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
loadBill();
|
|
178
|
+
}, [billId, setItems]);
|
|
179
|
+
|
|
180
|
+
const billNumber = bill?.receiptNumber || bill?.id || '';
|
|
181
|
+
const cashPoint = bill?.cashPoint?.name || '';
|
|
182
|
+
const billState = bill?.status || '';
|
|
183
|
+
const patientName = bill?.patient?.display || '';
|
|
184
|
+
|
|
185
|
+
const [logs, setLogs] = useState<PaymentLog[]>([]);
|
|
186
|
+
const [modalType, setModalType] = useState<
|
|
187
|
+
'CASH' | 'WAIVER' | 'SHA' | 'CHECK_SHA' | 'EDIT_QUANTITY' | 'CONFIRM_DELETE' | null
|
|
188
|
+
>(null);
|
|
189
|
+
const [reference, setReference] = useState('');
|
|
190
|
+
const [waiverType, setWaiverType] = useState<'PERCENT' | 'AMOUNT'>('AMOUNT');
|
|
191
|
+
|
|
192
|
+
const [waiverPercent, setWaiverPercent] = useState('');
|
|
193
|
+
const [waiverAmount, setWaiverAmount] = useState('');
|
|
194
|
+
const [editItem, setEditItem] = useState<LineItem | null>(null);
|
|
195
|
+
const [editQuantity, setEditQuantity] = useState<number>(1);
|
|
196
|
+
|
|
197
|
+
const [claimResponse, setClaimResponse] = useState<any | null>(null);
|
|
198
|
+
const [checkingClaim, setCheckingClaim] = useState(false);
|
|
199
|
+
|
|
200
|
+
const percent = Number(waiverPercent || 0);
|
|
201
|
+
const amount = Number(waiverAmount || 0);
|
|
202
|
+
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
setWaiverPercent('');
|
|
205
|
+
setWaiverAmount('');
|
|
206
|
+
}, [waiverType]);
|
|
207
|
+
|
|
208
|
+
const getBalance = (i: LineItem) =>
|
|
209
|
+
i.price * i.quantity - (i.paidQuantity || 0) * i.price - (i.waivedAmount || 0) - (i.claimedAmount || 0);
|
|
210
|
+
|
|
211
|
+
const toggleSelect = (id: string) =>
|
|
212
|
+
setItems((prev) => prev.map((i) => (i.id === id ? { ...i, selected: !i.selected } : i)));
|
|
213
|
+
const deleteItem = (id: string) => setItems((prev) => prev.filter((i) => i.id !== id));
|
|
214
|
+
|
|
215
|
+
const addLog = (type: 'CASH' | 'WAIVER' | 'SHA', affectedItems: LineItem[], totalAmount: number) => {
|
|
216
|
+
setLogs((prev) => [
|
|
217
|
+
...prev,
|
|
218
|
+
{
|
|
219
|
+
id: `${type}-${prev.length + 1}`,
|
|
220
|
+
type,
|
|
221
|
+
items: affectedItems,
|
|
222
|
+
totalAmount,
|
|
223
|
+
reference: type === 'CASH' ? reference : undefined,
|
|
224
|
+
timestamp: new Date().toLocaleString(),
|
|
225
|
+
},
|
|
226
|
+
]);
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
// SHA flags
|
|
230
|
+
const shaOnlyBill = items.length > 0 && items.every((i) => (i.payerType || '').toUpperCase() === 'SHA');
|
|
231
|
+
|
|
232
|
+
const pendingShaItems = items.filter(
|
|
233
|
+
(i) => i.status === 'PENDING' && INSURANCE_MODES.includes(i.payerType.toUpperCase()),
|
|
234
|
+
);
|
|
235
|
+
|
|
236
|
+
const hasPendingSha = pendingShaItems.length > 0;
|
|
237
|
+
|
|
238
|
+
const isSha = (i: LineItem) => INSURANCE_MODES.includes((i.payerType ?? '').toUpperCase());
|
|
239
|
+
const status = (i: LineItem) => (i.status ?? '').toUpperCase();
|
|
240
|
+
|
|
241
|
+
const claimedShaItems = items.filter((i) => isSha(i) && ['CLAIMED', 'PAID'].includes(status(i)));
|
|
242
|
+
|
|
243
|
+
const hasClaimedSha = claimedShaItems.length > 0;
|
|
244
|
+
|
|
245
|
+
// All Cash items fully paid
|
|
246
|
+
const allCashSettled = items
|
|
247
|
+
.filter((i) => (i.payerType ?? '').toUpperCase() === 'CASH')
|
|
248
|
+
.every((i) => (i.status ?? '').toUpperCase() === 'PAID');
|
|
249
|
+
|
|
250
|
+
const selectedCashItems = items.filter((i) => i.selected && i.payerType === 'CASH' && i.status === 'PENDING');
|
|
251
|
+
|
|
252
|
+
// const totalBalance = items.reduce((acc, i) => acc + getBalance(i), 0);
|
|
253
|
+
const totalPaid = items.reduce((acc, i) => {
|
|
254
|
+
const payer = (i.payerType ?? '').toString().trim().toUpperCase();
|
|
255
|
+
const status = (i.status ?? '').toString().trim().toUpperCase();
|
|
256
|
+
const qty = Number(i.quantity ?? 0);
|
|
257
|
+
const price = Number(i.price ?? 0);
|
|
258
|
+
|
|
259
|
+
if (payer === 'CASH' && status === 'PAID') {
|
|
260
|
+
return acc + qty * price;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return acc;
|
|
264
|
+
}, 0);
|
|
265
|
+
|
|
266
|
+
const billTotal = items.reduce((acc, i) => acc + i.price * i.quantity, 0);
|
|
267
|
+
const totalWaived = items.reduce((acc, i) => acc + (i.waivedAmount || 0), 0);
|
|
268
|
+
|
|
269
|
+
const totalClaimed = items.reduce((acc, i) => {
|
|
270
|
+
const payer = (i.payerType ?? '').toUpperCase();
|
|
271
|
+
const status = (i.status ?? '').toUpperCase();
|
|
272
|
+
|
|
273
|
+
if (INSURANCE_MODES.includes(payer) && (status === 'CLAIMED' || status === 'PAID')) {
|
|
274
|
+
return acc + (i.claimedAmount ?? i.price * i.quantity);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return acc;
|
|
278
|
+
}, 0);
|
|
279
|
+
|
|
280
|
+
const schemes = [...new Set(pendingShaItems.map((i) => i.payerType))].join(', ');
|
|
281
|
+
|
|
282
|
+
const showAlert = (type: string, title: string, subTitle: string) => {
|
|
283
|
+
showSnackbar({
|
|
284
|
+
kind: type,
|
|
285
|
+
title: title,
|
|
286
|
+
subtitle: subTitle,
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
// Balance per item: remaining amount to be settled
|
|
291
|
+
const totalBalance = billTotal - (totalPaid + totalClaimed + totalWaived);
|
|
292
|
+
|
|
293
|
+
// Auto-select SHA items
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
setItems((prev) => {
|
|
296
|
+
if (prev.length === 0) return prev;
|
|
297
|
+
|
|
298
|
+
return prev.map((i) => {
|
|
299
|
+
if (shaOnlyBill) return { ...i, selected: true };
|
|
300
|
+
|
|
301
|
+
if (i.status === 'PENDING' && i.payerType.toUpperCase() === 'SHA') {
|
|
302
|
+
return { ...i, selected: true };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return i;
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
}, [shaOnlyBill]);
|
|
309
|
+
|
|
310
|
+
const crId =
|
|
311
|
+
bill?.patient?.identifiers?.find((id: any) => id.identifierType?.display?.toLowerCase().includes('cr'))
|
|
312
|
+
?.identifier || '';
|
|
313
|
+
|
|
314
|
+
const locationUuid = bill?.cashPoint?.location?.uuid || '';
|
|
315
|
+
|
|
316
|
+
// ----- Render Raise SHA Claim Button -----
|
|
317
|
+
const showShaClaimButton = () => {
|
|
318
|
+
// Show Raise Claim if pending SHA exists
|
|
319
|
+
if (hasPendingSha && (shaOnlyBill || allCashSettled)) {
|
|
320
|
+
return 'RAISE';
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Show Check Status if all SHA items are already claimed/paid
|
|
324
|
+
if (!hasPendingSha && hasClaimedSha) {
|
|
325
|
+
return 'STATUS';
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const statusTag = (status: LineItemStatus) => {
|
|
332
|
+
switch (status) {
|
|
333
|
+
case 'PAID':
|
|
334
|
+
return <Tag type="green">Paid (Cash)</Tag>;
|
|
335
|
+
case 'CLAIMED':
|
|
336
|
+
return <Tag type="blue">Claimed</Tag>;
|
|
337
|
+
case 'WAIVED':
|
|
338
|
+
return <Tag type="purple">Waived</Tag>;
|
|
339
|
+
default:
|
|
340
|
+
return <Tag type="red">Pending</Tag>;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
// Prepare transaction rows
|
|
345
|
+
const transactionRows = logs.map((l) => ({
|
|
346
|
+
id: l.id,
|
|
347
|
+
timestamp: l.timestamp,
|
|
348
|
+
type: l.type,
|
|
349
|
+
items: l.items.map((i) => i.service).join(', '),
|
|
350
|
+
totalAmount: l.totalAmount,
|
|
351
|
+
reference: l.reference ?? l.claimNumber ?? '-',
|
|
352
|
+
}));
|
|
353
|
+
|
|
354
|
+
const billStatus = () => {
|
|
355
|
+
if (totalBalance === 0) return 'Fully Settled';
|
|
356
|
+
if (totalBalance < billTotal) return 'Partially Settled';
|
|
357
|
+
return 'Unpaid';
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
const processCashPayment = async () => {
|
|
361
|
+
if (!reference.trim()) return showAlert('error', 'Cash Payment', 'Payment reference required');
|
|
362
|
+
|
|
363
|
+
if (selectedCashItems.length === 0) showAlert('error', 'Cash Payment', 'No items selected');
|
|
364
|
+
|
|
365
|
+
try {
|
|
366
|
+
setCashLoading(true);
|
|
367
|
+
// 1️⃣ Fetch payment modes and find Cash UUID
|
|
368
|
+
const paymentModesResponse = await fetchPaymentModes();
|
|
369
|
+
|
|
370
|
+
// Find the UUID for Cash
|
|
371
|
+
const cashMode = paymentModesResponse.results.find((mode) => mode.name.toLowerCase() === 'cash');
|
|
372
|
+
if (!cashMode) {
|
|
373
|
+
setCashLoading(false);
|
|
374
|
+
showAlert('error', 'Cash Payment', 'Cash payment mode not found');
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const cashUuid = cashMode.uuid;
|
|
378
|
+
|
|
379
|
+
// 2️⃣ Prepare payload for line item payments
|
|
380
|
+
const payload = {
|
|
381
|
+
instanceType: cashUuid,
|
|
382
|
+
amount: selectedCashItems.reduce((acc, i) => acc + getBalance(i), 0),
|
|
383
|
+
amountTendered: selectedCashItems.reduce((acc, i) => acc + getBalance(i), 0),
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
// 3️⃣ Call payment endpoint
|
|
387
|
+
const response = await processPayment(billId, payload);
|
|
388
|
+
if (!response.ok) {
|
|
389
|
+
setCashLoading(false);
|
|
390
|
+
showAlert('error', 'Cash Payment', 'Payment processing failed');
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const statusPayload = {
|
|
395
|
+
billUuid: billId,
|
|
396
|
+
cashModeUuid: cashUuid,
|
|
397
|
+
billItemsUuid: selectedCashItems.map((item) => item.id),
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const statusResponse = await UpdateBillItemStatus(statusPayload);
|
|
401
|
+
if (!statusResponse?.success) {
|
|
402
|
+
setCashLoading(false);
|
|
403
|
+
showAlert('error', 'Cash Payment', statusResponse?.message ?? 'Status update failed');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
showAlert('success', 'Cash Payment', 'Payment processed successfully');
|
|
408
|
+
|
|
409
|
+
// 4️⃣ Re-fetch bill and update local state instead of manual update
|
|
410
|
+
const refreshedBill = await fetchBillById(billId);
|
|
411
|
+
|
|
412
|
+
setBill(refreshedBill);
|
|
413
|
+
|
|
414
|
+
const mappedItems: LineItem[] =
|
|
415
|
+
refreshedBill?.lineItems?.map((li: any) => ({
|
|
416
|
+
id: li.uuid,
|
|
417
|
+
service: li.billableService || 'Service',
|
|
418
|
+
payerType: (li.priceName && li.priceName.toUpperCase()) || 'CASH',
|
|
419
|
+
supportsSha: li.supportsSha || false,
|
|
420
|
+
waiverAllowed: li.waiverAllowed || false,
|
|
421
|
+
quantity: li.quantity || 1,
|
|
422
|
+
price: li.price || 0,
|
|
423
|
+
status: (li.paymentStatus && li.paymentStatus.toUpperCase()) || 'CASH',
|
|
424
|
+
})) || [];
|
|
425
|
+
|
|
426
|
+
setItems(mappedItems);
|
|
427
|
+
|
|
428
|
+
addLog('CASH', selectedCashItems, payload.amountTendered);
|
|
429
|
+
|
|
430
|
+
setReference('');
|
|
431
|
+
setModalType(null);
|
|
432
|
+
setCashLoading(false);
|
|
433
|
+
} catch (error) {
|
|
434
|
+
console.error('Payment failed', error);
|
|
435
|
+
setCashLoading(false);
|
|
436
|
+
showAlert('error', 'Cash Payment', 'Payment failed. Please try again.');
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
const handleCheckSHAClaim = async () => {
|
|
441
|
+
if (!billId) return showSnackbar({ kind: 'error', title: 'SHA Claim Status', subtitle: 'Bill ID is required' });
|
|
442
|
+
|
|
443
|
+
setCheckingClaim(true);
|
|
444
|
+
|
|
445
|
+
const res = await checkClaimStatus(billId);
|
|
446
|
+
|
|
447
|
+
if (!res.success) {
|
|
448
|
+
setCheckingClaim(false);
|
|
449
|
+
return showSnackbar({
|
|
450
|
+
kind: 'error',
|
|
451
|
+
title: 'SHA Claim Status',
|
|
452
|
+
subtitle: res.message,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
setClaimResponse(res.data);
|
|
457
|
+
setModalType('CHECK_SHA');
|
|
458
|
+
setCheckingClaim(false);
|
|
459
|
+
};
|
|
460
|
+
|
|
461
|
+
const applyWaiver = async () => {
|
|
462
|
+
return showAlert(
|
|
463
|
+
'error',
|
|
464
|
+
'Waiver Application',
|
|
465
|
+
'Waiver functionality is currently unavailable. Please contact support.',
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
setWaiverLoading(true);
|
|
469
|
+
|
|
470
|
+
const appliedAmount = waiverType === 'PERCENT' ? Math.round((totalBalance * percent) / 100) : waiverAmount;
|
|
471
|
+
if (amount <= 0) return;
|
|
472
|
+
showAlert('error', 'Waiver Application', 'Waiver amount must be greater than zero');
|
|
473
|
+
|
|
474
|
+
// 1️⃣ Fetch payment modes and find Cash UUID
|
|
475
|
+
const waiverModesResponse = await fetchPaymentModes();
|
|
476
|
+
|
|
477
|
+
// Find the UUID for Cash
|
|
478
|
+
const waiverMode = waiverModesResponse.results.find((mode) => mode.name.toLowerCase() === 'waiver');
|
|
479
|
+
if (!waiverMode) showAlert('error', 'Waiver Application', 'Waiver payment mode not found. Please contact support.');
|
|
480
|
+
|
|
481
|
+
const waiverUuid = waiverMode.uuid;
|
|
482
|
+
|
|
483
|
+
// 2️⃣ Prepare payload for line item payments
|
|
484
|
+
const payload = {
|
|
485
|
+
instanceType: waiverUuid,
|
|
486
|
+
amount: appliedAmount,
|
|
487
|
+
amountTendered: appliedAmount,
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// 3️⃣ Call payment endpoint
|
|
491
|
+
const response = await processPayment(billId, payload);
|
|
492
|
+
if (!response.ok)
|
|
493
|
+
showAlert('error', 'Waiver Application', 'Failed to apply waiver. Please try again or contact support.');
|
|
494
|
+
|
|
495
|
+
showAlert('success', 'Waiver Application', 'Waiver applied successfully');
|
|
496
|
+
|
|
497
|
+
// 4️⃣ Re-fetch bill and update local state instead of manual update
|
|
498
|
+
const refreshedBill = await fetchBillById(billId);
|
|
499
|
+
|
|
500
|
+
setBill(refreshedBill);
|
|
501
|
+
|
|
502
|
+
const mappedItems: LineItem[] =
|
|
503
|
+
refreshedBill?.lineItems?.map((li: any) => ({
|
|
504
|
+
id: li.uuid,
|
|
505
|
+
service: li.billableService || 'Service',
|
|
506
|
+
payerType: (li.priceName && li.priceName.toUpperCase()) || 'CASH',
|
|
507
|
+
supportsSha: li.supportsSha || false,
|
|
508
|
+
waiverAllowed: li.waiverAllowed || false,
|
|
509
|
+
quantity: li.quantity || 1,
|
|
510
|
+
price: li.price || 0,
|
|
511
|
+
status: (li.paymentStatus && li.paymentStatus.toUpperCase()) || 'CASH',
|
|
512
|
+
})) || [];
|
|
513
|
+
|
|
514
|
+
setItems(mappedItems);
|
|
515
|
+
|
|
516
|
+
addLog(
|
|
517
|
+
'WAIVER',
|
|
518
|
+
items.filter((i) => i.waiverAllowed),
|
|
519
|
+
amount,
|
|
520
|
+
);
|
|
521
|
+
setWaiverAmount('');
|
|
522
|
+
setWaiverPercent('');
|
|
523
|
+
setModalType(null);
|
|
524
|
+
setWaiverLoading(false);
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const shaButtonState = showShaClaimButton();
|
|
528
|
+
|
|
529
|
+
const applySHAClaim = async () => {
|
|
530
|
+
try {
|
|
531
|
+
setShaLoading(true);
|
|
532
|
+
|
|
533
|
+
const paymentModesResponse = await fetchPaymentModes();
|
|
534
|
+
const shaMode = paymentModesResponse.results.find((mode) => mode.name.toLowerCase() === 'sha');
|
|
535
|
+
|
|
536
|
+
const shaModes11 = paymentModesResponse.results.filter((mode) =>
|
|
537
|
+
INSURANCE_MODES.includes(mode.name.toUpperCase()),
|
|
538
|
+
);
|
|
539
|
+
|
|
540
|
+
if (!shaMode) {
|
|
541
|
+
showAlert('error', 'SHA Claim', 'SHA payment mode not found. Please contact support.');
|
|
542
|
+
setShaLoading(false);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// 1️⃣ Check if claim already exists
|
|
547
|
+
const claimStatus = await checkClaimStatus(billId);
|
|
548
|
+
let claimExists = false;
|
|
549
|
+
|
|
550
|
+
if (claimStatus.success && claimStatus.data) {
|
|
551
|
+
claimExists = true;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// 2️⃣ Raise claim if not existing
|
|
555
|
+
if (!claimExists) {
|
|
556
|
+
const claimResponse = await raiseSHAClaim(billId);
|
|
557
|
+
|
|
558
|
+
if (!claimResponse.success) {
|
|
559
|
+
showAlert('error', 'SHA Claim', claimResponse.message || 'Failed to raise SHA claim.');
|
|
560
|
+
setShaLoading(false);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// 3️⃣ Process SHA payment
|
|
566
|
+
const totalAmount = pendingShaItems.reduce((acc, i) => acc + getBalance(i), 0);
|
|
567
|
+
|
|
568
|
+
const payload = {
|
|
569
|
+
instanceType: shaMode.uuid,
|
|
570
|
+
amount: totalAmount,
|
|
571
|
+
amountTendered: totalAmount,
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
const paymentResponse = await processPayment(billId, payload);
|
|
575
|
+
|
|
576
|
+
if (!paymentResponse.ok) {
|
|
577
|
+
showAlert('error', 'SHA Claim', 'Payment failed after claim creation.');
|
|
578
|
+
setShaLoading(false);
|
|
579
|
+
return;
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 4️⃣ Refresh bill
|
|
583
|
+
const refreshedBill = await fetchBillById(billId);
|
|
584
|
+
setBill(refreshedBill);
|
|
585
|
+
|
|
586
|
+
const mappedItems: LineItem[] =
|
|
587
|
+
refreshedBill?.lineItems?.map((li: any) => ({
|
|
588
|
+
id: li.uuid,
|
|
589
|
+
service: li.billableService || 'Service',
|
|
590
|
+
payerType: (li.priceName && li.priceName.toUpperCase()) || 'CASH',
|
|
591
|
+
supportsSha: li.supportsSha || false,
|
|
592
|
+
waiverAllowed: li.waiverAllowed || false,
|
|
593
|
+
quantity: li.quantity || 1,
|
|
594
|
+
price: li.price || 0,
|
|
595
|
+
status: (li.paymentStatus?.toUpperCase() as LineItemStatus) || 'PENDING',
|
|
596
|
+
})) || [];
|
|
597
|
+
|
|
598
|
+
setItems(mappedItems);
|
|
599
|
+
|
|
600
|
+
// 5️⃣ Add transaction log
|
|
601
|
+
addLog('SHA', pendingShaItems, totalAmount);
|
|
602
|
+
|
|
603
|
+
showAlert('success', 'SHA Claim', 'SHA claim processed successfully');
|
|
604
|
+
|
|
605
|
+
setModalType(null);
|
|
606
|
+
} catch (error) {
|
|
607
|
+
console.error('SHA claim flow failed', error);
|
|
608
|
+
showAlert('error', 'SHA Claim', 'Unexpected error while processing SHA claim.');
|
|
609
|
+
} finally {
|
|
610
|
+
setShaLoading(false);
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const openEditQuantity = (item: LineItem) => {
|
|
615
|
+
setEditItem(item);
|
|
616
|
+
setEditQuantity(item.quantity);
|
|
617
|
+
setModalType('EDIT_QUANTITY');
|
|
618
|
+
};
|
|
619
|
+
const saveEditQuantity = () => {
|
|
620
|
+
if (!editItem) return;
|
|
621
|
+
if (editQuantity < 1) return alert('Quantity must be at least 1');
|
|
622
|
+
setItems((prev) => prev.map((i) => (i.id === editItem.id ? { ...i, quantity: editQuantity } : i)));
|
|
623
|
+
setModalType(null);
|
|
624
|
+
};
|
|
625
|
+
|
|
626
|
+
const statusText = `${billState}${billStatus() ? ` (${billStatus()})` : ''}`;
|
|
627
|
+
|
|
628
|
+
const [cashLoading, setCashLoading] = useState(false);
|
|
629
|
+
const [waiverLoading, setWaiverLoading] = useState(false);
|
|
630
|
+
const [shaLoading, setShaLoading] = useState(false);
|
|
631
|
+
|
|
632
|
+
const handlePrintInvoice = () => {
|
|
633
|
+
// For partially paid / unpaid bills
|
|
634
|
+
const billUrl = `/billing/print-invoice/${billId}`;
|
|
635
|
+
window.open(billUrl, '_blank');
|
|
636
|
+
};
|
|
637
|
+
|
|
638
|
+
const handlePrintReceipt = () => {
|
|
639
|
+
// For fully paid bills
|
|
640
|
+
const receiptUrl = `/billing/print-receipt/${billId}`;
|
|
641
|
+
window.open(receiptUrl, '_blank');
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
const finalizeBill = () => {
|
|
645
|
+
const confirmed = window.confirm('Finalize bill? This action cannot be undone.');
|
|
646
|
+
if (confirmed) {
|
|
647
|
+
alert('Bill finalized! (This is a placeholder action)');
|
|
648
|
+
// handlePrintReceipt();
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
|
|
652
|
+
const getClaimTagType = (status?: string) => {
|
|
653
|
+
const s = status?.toLowerCase() || '';
|
|
654
|
+
|
|
655
|
+
if (s.includes('approved') || s.includes('paid')) return 'green';
|
|
656
|
+
if (s.includes('pending')) return 'warm-gray';
|
|
657
|
+
if (s.includes('rejected') || s.includes('failed')) return 'red';
|
|
658
|
+
|
|
659
|
+
return 'cool-gray';
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
const handleRaiseShaConfirm = () => {
|
|
663
|
+
const confirmed = window.confirm(
|
|
664
|
+
'Raise SHA claim for this bill?\n\nConfirm that all cash items are settled if any and that there are no additional items to add to the claim.',
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
if (confirmed) {
|
|
668
|
+
setModalType('SHA');
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
return (
|
|
673
|
+
<Grid fullWidth style={{ padding: '0' }}>
|
|
674
|
+
{/* Breadcrumb & Bill Info */}
|
|
675
|
+
<Column lg={16} style={{ marginTop: '1rem' }}>
|
|
676
|
+
<Breadcrumb style={{ marginBottom: '1rem' }}>
|
|
677
|
+
<BreadcrumbItem onClick={navigateToBillingPage}>Billing Dashboard</BreadcrumbItem>
|
|
678
|
+
<BreadcrumbItem isCurrentPage>{loading ? <SkeletonText width="120px" /> : billNumber}</BreadcrumbItem>
|
|
679
|
+
</Breadcrumb>
|
|
680
|
+
|
|
681
|
+
<Tile style={{ paddingTop: '1rem' }}>
|
|
682
|
+
{/* Patient Name on Top */}
|
|
683
|
+
{loading ? (
|
|
684
|
+
<SkeletonText width="200px" />
|
|
685
|
+
) : (
|
|
686
|
+
<>
|
|
687
|
+
<h5 style={{ marginBottom: '0.5rem' }}>{patientName}</h5>
|
|
688
|
+
|
|
689
|
+
{crId && locationUuid && <EligibilityTags crId={crId} locationUuid={locationUuid} />}
|
|
690
|
+
</>
|
|
691
|
+
)}
|
|
692
|
+
|
|
693
|
+
{/* Row with three columns */}
|
|
694
|
+
<Grid fullWidth>
|
|
695
|
+
<Column sm={4} md={4} lg={4}>
|
|
696
|
+
<p>
|
|
697
|
+
<strong>Bill No:</strong> {loading ? <SkeletonText width="100px" /> : billNumber}
|
|
698
|
+
</p>
|
|
699
|
+
</Column>
|
|
700
|
+
<Column sm={4} md={4} lg={4}>
|
|
701
|
+
<p>
|
|
702
|
+
<strong>Cash Point:</strong> {loading ? <SkeletonText width="120px" /> : cashPoint}
|
|
703
|
+
</p>
|
|
704
|
+
</Column>
|
|
705
|
+
<Column sm={4} md={4} lg={4}>
|
|
706
|
+
<p>
|
|
707
|
+
<strong>Bill Date:</strong>{' '}
|
|
708
|
+
{loading ? <SkeletonText width="150px" /> : formatBillDate(bill?.dateCreated)}
|
|
709
|
+
</p>
|
|
710
|
+
</Column>
|
|
711
|
+
</Grid>
|
|
712
|
+
</Tile>
|
|
713
|
+
</Column>
|
|
714
|
+
|
|
715
|
+
{/* ===== Bordered Main Section ===== */}
|
|
716
|
+
<Column lg={16}>
|
|
717
|
+
<div
|
|
718
|
+
style={{
|
|
719
|
+
borderTop: '1px solid #e0e0e0',
|
|
720
|
+
paddingTop: '1rem',
|
|
721
|
+
marginTop: '1rem',
|
|
722
|
+
background: '#fff',
|
|
723
|
+
}}
|
|
724
|
+
>
|
|
725
|
+
<Grid fullWidth>
|
|
726
|
+
{/* Left Section */}
|
|
727
|
+
<Column lg={12}>
|
|
728
|
+
<Tile>
|
|
729
|
+
{!allCashSettled && (
|
|
730
|
+
<Tile
|
|
731
|
+
style={{
|
|
732
|
+
background: '#fff5f5',
|
|
733
|
+
border: '1px solid #f5c2c2',
|
|
734
|
+
borderRadius: '8px',
|
|
735
|
+
padding: '1rem',
|
|
736
|
+
marginBottom: '1rem',
|
|
737
|
+
width: '100%',
|
|
738
|
+
}}
|
|
739
|
+
>
|
|
740
|
+
<p
|
|
741
|
+
style={{
|
|
742
|
+
color: '#da1e28',
|
|
743
|
+
margin: 0,
|
|
744
|
+
fontSize: '0.9rem',
|
|
745
|
+
lineHeight: 1.5,
|
|
746
|
+
}}
|
|
747
|
+
>
|
|
748
|
+
Please select cash item(s) to continue. SHA claims (if any) will be available after all cash
|
|
749
|
+
payments are completed.
|
|
750
|
+
</p>
|
|
751
|
+
</Tile>
|
|
752
|
+
)}
|
|
753
|
+
|
|
754
|
+
<div
|
|
755
|
+
style={{
|
|
756
|
+
display: 'flex',
|
|
757
|
+
justifyContent: 'space-between',
|
|
758
|
+
alignItems: 'center',
|
|
759
|
+
marginBottom: '1rem',
|
|
760
|
+
}}
|
|
761
|
+
>
|
|
762
|
+
<h4>
|
|
763
|
+
<u>Bill Items</u>
|
|
764
|
+
</h4>
|
|
765
|
+
<Stack orientation="horizontal" gap={2}>
|
|
766
|
+
{selectedCashItems.length > 0 && (
|
|
767
|
+
<Button disabled={loading} renderIcon={Money} size="sm" onClick={() => setModalType('CASH')}>
|
|
768
|
+
Pay Selected Cash Items
|
|
769
|
+
</Button>
|
|
770
|
+
)}
|
|
771
|
+
|
|
772
|
+
{shaButtonState === 'RAISE' && (
|
|
773
|
+
<Button
|
|
774
|
+
disabled={loading || checkingClaim}
|
|
775
|
+
size="sm"
|
|
776
|
+
kind="secondary"
|
|
777
|
+
renderIcon={DocumentAdd}
|
|
778
|
+
onClick={handleRaiseShaConfirm}
|
|
779
|
+
>
|
|
780
|
+
Raise SHA Claim
|
|
781
|
+
</Button>
|
|
782
|
+
)}
|
|
783
|
+
|
|
784
|
+
{shaButtonState === 'STATUS' && (
|
|
785
|
+
<Button
|
|
786
|
+
disabled={loading || checkingClaim}
|
|
787
|
+
size="sm"
|
|
788
|
+
kind="tertiary"
|
|
789
|
+
renderIcon={View}
|
|
790
|
+
onClick={handleCheckSHAClaim}
|
|
791
|
+
>
|
|
792
|
+
{checkingClaim ? 'Checking Status...' : 'Check SHA Claim Status'}
|
|
793
|
+
</Button>
|
|
794
|
+
)}
|
|
795
|
+
|
|
796
|
+
{totalBalance > 0 && (
|
|
797
|
+
<Button
|
|
798
|
+
disabled={loading}
|
|
799
|
+
renderIcon={Subtract}
|
|
800
|
+
size="sm"
|
|
801
|
+
kind="tertiary"
|
|
802
|
+
onClick={() => setModalType('WAIVER')}
|
|
803
|
+
>
|
|
804
|
+
Apply Waiver
|
|
805
|
+
</Button>
|
|
806
|
+
)}
|
|
807
|
+
|
|
808
|
+
{totalBalance === 0 && billState === 'PENDING' && (
|
|
809
|
+
<Button size="sm" kind="primary" renderIcon={ResultNew} onClick={finalizeBill}>
|
|
810
|
+
Finalize Bill
|
|
811
|
+
</Button>
|
|
812
|
+
)}
|
|
813
|
+
|
|
814
|
+
{totalBalance === 0 && billState === 'PAID' && (
|
|
815
|
+
<Button disabled size="sm" kind="tertiary" renderIcon={Receipt} onClick={handlePrintReceipt}>
|
|
816
|
+
Print Receipt
|
|
817
|
+
</Button>
|
|
818
|
+
)}
|
|
819
|
+
</Stack>
|
|
820
|
+
</div>
|
|
821
|
+
|
|
822
|
+
{/* Bill Items Table */}
|
|
823
|
+
{loading ? (
|
|
824
|
+
<SkeletonPlaceholder style={{ height: '300px', width: '100%' }} />
|
|
825
|
+
) : (
|
|
826
|
+
<DataTable rows={items} headers={headers}>
|
|
827
|
+
{({ rows, headers, getHeaderProps, getRowProps }) => (
|
|
828
|
+
<Table>
|
|
829
|
+
<TableHead>
|
|
830
|
+
<TableRow>
|
|
831
|
+
{headers.map((h) => (
|
|
832
|
+
<TableHeader key={h.key} {...getHeaderProps({ header: h })}>
|
|
833
|
+
{h.header}
|
|
834
|
+
</TableHeader>
|
|
835
|
+
))}
|
|
836
|
+
</TableRow>
|
|
837
|
+
</TableHead>
|
|
838
|
+
|
|
839
|
+
<TableBody>
|
|
840
|
+
{rows.map((row) => {
|
|
841
|
+
const i = items.find((it) => it.id === row.id)!;
|
|
842
|
+
|
|
843
|
+
return (
|
|
844
|
+
<TableRow key={i.id} {...getRowProps({ row })}>
|
|
845
|
+
<TableCell>{i.service}</TableCell>
|
|
846
|
+
<TableCell>{i.payerType}</TableCell>
|
|
847
|
+
<TableCell>{i.quantity}</TableCell>
|
|
848
|
+
<TableCell>Ksh {i.price}</TableCell>
|
|
849
|
+
<TableCell>Ksh {getBalance(i)}</TableCell>
|
|
850
|
+
<TableCell>
|
|
851
|
+
{i.payerType.toUpperCase() === 'SHA' && i.status.toUpperCase() === 'PAID'
|
|
852
|
+
? statusTag('CLAIMED')
|
|
853
|
+
: statusTag(i.status)}
|
|
854
|
+
</TableCell>
|
|
855
|
+
|
|
856
|
+
<TableCell>
|
|
857
|
+
<Checkbox
|
|
858
|
+
id={`select-${i.id}`}
|
|
859
|
+
labelText=""
|
|
860
|
+
checked={shaOnlyBill ? true : !!i.selected}
|
|
861
|
+
disabled={i.status !== 'PENDING' || shaOnlyBill || i.payerType !== 'CASH'}
|
|
862
|
+
onChange={() => toggleSelect(i.id)}
|
|
863
|
+
/>
|
|
864
|
+
</TableCell>
|
|
865
|
+
|
|
866
|
+
<TableCell>
|
|
867
|
+
<OverflowMenu ariaLabel="Actions" size="sm" disabled={i.status !== 'PENDING'}>
|
|
868
|
+
<OverflowMenuItem itemText="Edit Quantity" onClick={() => openEditQuantity(i)} />
|
|
869
|
+
<OverflowMenuItem
|
|
870
|
+
itemText="Delete Item"
|
|
871
|
+
onClick={() => {
|
|
872
|
+
setEditItem(i);
|
|
873
|
+
setModalType('CONFIRM_DELETE');
|
|
874
|
+
}}
|
|
875
|
+
/>
|
|
876
|
+
</OverflowMenu>
|
|
877
|
+
</TableCell>
|
|
878
|
+
</TableRow>
|
|
879
|
+
);
|
|
880
|
+
})}
|
|
881
|
+
</TableBody>
|
|
882
|
+
</Table>
|
|
883
|
+
)}
|
|
884
|
+
</DataTable>
|
|
885
|
+
)}
|
|
886
|
+
</Tile>
|
|
887
|
+
|
|
888
|
+
{bill?.payments?.length > 0 && (
|
|
889
|
+
<Tile style={{ marginTop: '1rem' }}>
|
|
890
|
+
<h4 style={{ marginBottom: '1rem' }}>
|
|
891
|
+
<u>Payment Details</u>
|
|
892
|
+
</h4>
|
|
893
|
+
|
|
894
|
+
<DataTable
|
|
895
|
+
rows={bill.payments.map((p: any) => ({
|
|
896
|
+
id: p.uuid,
|
|
897
|
+
type: p.instanceType?.name || '-',
|
|
898
|
+
amount: formatCurrency(p.amount),
|
|
899
|
+
amountTendered: p.amountTendered ? formatCurrency(p.amountTendered) : '-',
|
|
900
|
+
dateCreated: formatBillDate(p.dateCreated),
|
|
901
|
+
}))}
|
|
902
|
+
headers={[
|
|
903
|
+
{ key: 'type', header: 'Payment Type' },
|
|
904
|
+
{ key: 'amount', header: 'Amount' },
|
|
905
|
+
{ key: 'amountTendered', header: 'Amount Tendered' },
|
|
906
|
+
{ key: 'dateCreated', header: 'Date / Time' },
|
|
907
|
+
]}
|
|
908
|
+
>
|
|
909
|
+
{({ rows, headers, getHeaderProps, getRowProps }) => (
|
|
910
|
+
<Table>
|
|
911
|
+
<TableHead>
|
|
912
|
+
<TableRow>
|
|
913
|
+
{headers.map((h) => (
|
|
914
|
+
<TableHeader key={h.key} {...getHeaderProps({ header: h })}>
|
|
915
|
+
{h.header}
|
|
916
|
+
</TableHeader>
|
|
917
|
+
))}
|
|
918
|
+
</TableRow>
|
|
919
|
+
</TableHead>
|
|
920
|
+
|
|
921
|
+
<TableBody>
|
|
922
|
+
{rows.map((row) => (
|
|
923
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
924
|
+
{row.cells.map((cell) => (
|
|
925
|
+
<TableCell key={cell.id}>{cell.value || '-'}</TableCell>
|
|
926
|
+
))}
|
|
927
|
+
</TableRow>
|
|
928
|
+
))}
|
|
929
|
+
</TableBody>
|
|
930
|
+
</Table>
|
|
931
|
+
)}
|
|
932
|
+
</DataTable>
|
|
933
|
+
</Tile>
|
|
934
|
+
)}
|
|
935
|
+
</Column>
|
|
936
|
+
|
|
937
|
+
{/* Right Summary */}
|
|
938
|
+
<Column lg={4}>
|
|
939
|
+
<Tile style={{ padding: '1.5rem' }}>
|
|
940
|
+
<h4 style={{ marginBottom: '1rem' }}>
|
|
941
|
+
<u>Bill Summary</u>
|
|
942
|
+
</h4>
|
|
943
|
+
|
|
944
|
+
<Stack gap={3}>
|
|
945
|
+
{/* Status */}
|
|
946
|
+
{loading ? (
|
|
947
|
+
<SkeletonText width="100%" />
|
|
948
|
+
) : (
|
|
949
|
+
<Tag type={totalBalance === 0 ? 'green' : totalBalance < billTotal ? 'teal' : 'red'}>
|
|
950
|
+
{totalBalance === 0 && <CheckmarkFilled size={12} style={{ marginRight: 4 }} />}
|
|
951
|
+
{totalBalance > 0 && totalBalance < billTotal && (
|
|
952
|
+
<WarningAlt size={12} style={{ marginRight: 4 }} />
|
|
953
|
+
)}
|
|
954
|
+
{totalBalance === billTotal && <PendingFilled size={12} style={{ marginRight: 4 }} />}
|
|
955
|
+
{statusText}
|
|
956
|
+
</Tag>
|
|
957
|
+
)}
|
|
958
|
+
|
|
959
|
+
{/* Divider */}
|
|
960
|
+
<hr style={{ border: 'none', borderTop: '1px solid #e0e0e0' }} />
|
|
961
|
+
|
|
962
|
+
{/* Total */}
|
|
963
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
964
|
+
<span style={{ fontWeight: 'bold' }}>Total</span>
|
|
965
|
+
<strong>
|
|
966
|
+
{loading ? (
|
|
967
|
+
<SkeletonText width="100px" />
|
|
968
|
+
) : (
|
|
969
|
+
`Ksh ${Number(billTotal).toLocaleString(undefined, {
|
|
970
|
+
minimumFractionDigits: 2,
|
|
971
|
+
maximumFractionDigits: 2,
|
|
972
|
+
})}`
|
|
973
|
+
)}
|
|
974
|
+
</strong>
|
|
975
|
+
</div>
|
|
976
|
+
|
|
977
|
+
{/* Paid */}
|
|
978
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
979
|
+
<span style={{ color: '#24a148', fontWeight: 'bold' }}>Paid</span>
|
|
980
|
+
<strong style={{ color: '#24a148' }}>
|
|
981
|
+
{loading ? (
|
|
982
|
+
<SkeletonText width="100px" />
|
|
983
|
+
) : (
|
|
984
|
+
`Ksh ${Number(totalPaid).toLocaleString(undefined, {
|
|
985
|
+
minimumFractionDigits: 2,
|
|
986
|
+
maximumFractionDigits: 2,
|
|
987
|
+
})}`
|
|
988
|
+
)}
|
|
989
|
+
</strong>
|
|
990
|
+
</div>
|
|
991
|
+
|
|
992
|
+
{/* Claimed */}
|
|
993
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
994
|
+
<span style={{ color: '#0f62fe', fontWeight: 'bold' }}>Claimed</span>
|
|
995
|
+
<strong style={{ color: '#0f62fe' }}>
|
|
996
|
+
{loading ? (
|
|
997
|
+
<SkeletonText width="100px" />
|
|
998
|
+
) : (
|
|
999
|
+
`Ksh ${Number(totalClaimed).toLocaleString(undefined, {
|
|
1000
|
+
minimumFractionDigits: 2,
|
|
1001
|
+
maximumFractionDigits: 2,
|
|
1002
|
+
})}`
|
|
1003
|
+
)}
|
|
1004
|
+
</strong>
|
|
1005
|
+
</div>
|
|
1006
|
+
|
|
1007
|
+
{/* Waived */}
|
|
1008
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
1009
|
+
<span style={{ color: '#8a3ffc', fontWeight: 'bold' }}>Waived</span>
|
|
1010
|
+
<strong style={{ color: '#8a3ffc' }}>
|
|
1011
|
+
{loading ? (
|
|
1012
|
+
<SkeletonText width="100px" />
|
|
1013
|
+
) : (
|
|
1014
|
+
`Ksh ${Number(totalWaived).toLocaleString(undefined, {
|
|
1015
|
+
minimumFractionDigits: 2,
|
|
1016
|
+
maximumFractionDigits: 2,
|
|
1017
|
+
})}`
|
|
1018
|
+
)}
|
|
1019
|
+
</strong>
|
|
1020
|
+
</div>
|
|
1021
|
+
|
|
1022
|
+
{/* Divider */}
|
|
1023
|
+
<hr style={{ border: 'none', borderTop: '1px solid #e0e0e0' }} />
|
|
1024
|
+
|
|
1025
|
+
{/* Balance */}
|
|
1026
|
+
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
|
1027
|
+
<span style={{ fontWeight: 'bold' }}>Balance</span>
|
|
1028
|
+
<strong
|
|
1029
|
+
style={{
|
|
1030
|
+
color: totalBalance > 0 ? '#da1e28' : '#24a148',
|
|
1031
|
+
fontSize: '1.1rem',
|
|
1032
|
+
}}
|
|
1033
|
+
>
|
|
1034
|
+
{loading ? (
|
|
1035
|
+
<SkeletonText width="100px" />
|
|
1036
|
+
) : (
|
|
1037
|
+
`Ksh ${Number(totalBalance).toLocaleString(undefined, {
|
|
1038
|
+
minimumFractionDigits: 2,
|
|
1039
|
+
maximumFractionDigits: 2,
|
|
1040
|
+
})}`
|
|
1041
|
+
)}
|
|
1042
|
+
</strong>
|
|
1043
|
+
</div>
|
|
1044
|
+
</Stack>
|
|
1045
|
+
</Tile>
|
|
1046
|
+
</Column>
|
|
1047
|
+
</Grid>
|
|
1048
|
+
</div>
|
|
1049
|
+
</Column>
|
|
1050
|
+
|
|
1051
|
+
{/* ================= MODALS ================= */}
|
|
1052
|
+
{/* Cash Payment */}
|
|
1053
|
+
<Modal
|
|
1054
|
+
open={modalType === 'CASH'}
|
|
1055
|
+
modalHeading="Cash Payment"
|
|
1056
|
+
primaryButtonText={cashLoading ? 'Processing...' : 'Submit'}
|
|
1057
|
+
secondaryButtonText="Cancel"
|
|
1058
|
+
primaryButtonDisabled={cashLoading}
|
|
1059
|
+
onRequestClose={() => setModalType(null)}
|
|
1060
|
+
onRequestSubmit={processCashPayment}
|
|
1061
|
+
>
|
|
1062
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
1063
|
+
<TextInput
|
|
1064
|
+
id="cash-payment-ref"
|
|
1065
|
+
labelText="Payment Reference"
|
|
1066
|
+
value={reference}
|
|
1067
|
+
onChange={(e) => setReference(e.target.value)}
|
|
1068
|
+
/>
|
|
1069
|
+
</div>
|
|
1070
|
+
|
|
1071
|
+
<ul>
|
|
1072
|
+
{selectedCashItems.map((i) => (
|
|
1073
|
+
<li key={i.id} style={{ marginBottom: '0.5rem' }}>
|
|
1074
|
+
{i.service} - Ksh {getBalance(i)}
|
|
1075
|
+
</li>
|
|
1076
|
+
))}
|
|
1077
|
+
</ul>
|
|
1078
|
+
|
|
1079
|
+
<p style={{ marginTop: '1rem' }}>
|
|
1080
|
+
<strong>Total: Ksh {selectedCashItems.reduce((acc, i) => acc + getBalance(i), 0)}</strong>
|
|
1081
|
+
</p>
|
|
1082
|
+
</Modal>
|
|
1083
|
+
|
|
1084
|
+
{/* Waiver */}
|
|
1085
|
+
<Modal
|
|
1086
|
+
open={modalType === 'WAIVER'}
|
|
1087
|
+
modalHeading="Apply Waiver"
|
|
1088
|
+
primaryButtonText={waiverLoading ? 'Applying...' : 'Submit'}
|
|
1089
|
+
secondaryButtonText="Cancel"
|
|
1090
|
+
primaryButtonDisabled={
|
|
1091
|
+
waiverLoading ||
|
|
1092
|
+
(waiverType === 'PERCENT'
|
|
1093
|
+
? waiverPercent === '' || Number(waiverPercent) < 1 || Number(waiverPercent) > 100
|
|
1094
|
+
: waiverAmount === '' || Number(waiverAmount) < 1)
|
|
1095
|
+
}
|
|
1096
|
+
onRequestClose={() => setModalType(null)}
|
|
1097
|
+
onRequestSubmit={applyWaiver}
|
|
1098
|
+
>
|
|
1099
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
1100
|
+
<label htmlFor="waiver-type-amount">
|
|
1101
|
+
<input
|
|
1102
|
+
type="radio"
|
|
1103
|
+
id="waiver-type-amount"
|
|
1104
|
+
name="waiver-type"
|
|
1105
|
+
value="AMOUNT"
|
|
1106
|
+
checked={waiverType === 'AMOUNT'}
|
|
1107
|
+
onChange={() => setWaiverType('AMOUNT')}
|
|
1108
|
+
/>{' '}
|
|
1109
|
+
Amount
|
|
1110
|
+
</label>
|
|
1111
|
+
|
|
1112
|
+
<label htmlFor="waiver-type-percent" style={{ marginLeft: '1rem' }}>
|
|
1113
|
+
<input
|
|
1114
|
+
type="radio"
|
|
1115
|
+
id="waiver-type-percent"
|
|
1116
|
+
name="waiver-type"
|
|
1117
|
+
value="PERCENT"
|
|
1118
|
+
checked={waiverType === 'PERCENT'}
|
|
1119
|
+
onChange={() => setWaiverType('PERCENT')}
|
|
1120
|
+
/>{' '}
|
|
1121
|
+
Percentage
|
|
1122
|
+
</label>
|
|
1123
|
+
</div>
|
|
1124
|
+
|
|
1125
|
+
{waiverType === 'PERCENT' ? (
|
|
1126
|
+
<NumberInput
|
|
1127
|
+
id="waiver-percent"
|
|
1128
|
+
label="Waiver Percentage"
|
|
1129
|
+
min={0}
|
|
1130
|
+
max={100}
|
|
1131
|
+
value={waiverPercent}
|
|
1132
|
+
invalid={waiverPercent !== '' && (Number(waiverPercent) < 1 || Number(waiverPercent) > 100)}
|
|
1133
|
+
invalidText="Percentage must be between 1 and 100"
|
|
1134
|
+
onChange={(_e, state) => setWaiverPercent(String(state.value))}
|
|
1135
|
+
/>
|
|
1136
|
+
) : (
|
|
1137
|
+
<NumberInput
|
|
1138
|
+
id="waiver-amount"
|
|
1139
|
+
label="Waiver Amount"
|
|
1140
|
+
min={0}
|
|
1141
|
+
value={waiverAmount}
|
|
1142
|
+
invalid={waiverAmount !== '' && Number(waiverAmount) < 1}
|
|
1143
|
+
invalidText="Amount cannot be less than 1"
|
|
1144
|
+
onChange={(_e, state) => setWaiverAmount(String(state.value))}
|
|
1145
|
+
/>
|
|
1146
|
+
)}
|
|
1147
|
+
</Modal>
|
|
1148
|
+
|
|
1149
|
+
{/* SHA Claim */}
|
|
1150
|
+
<Modal
|
|
1151
|
+
open={modalType === 'SHA'}
|
|
1152
|
+
modalHeading={`Confirm ${schemes} Claim`}
|
|
1153
|
+
primaryButtonText={shaLoading ? 'Submitting...' : 'Submit Claim'}
|
|
1154
|
+
secondaryButtonText="Cancel"
|
|
1155
|
+
primaryButtonDisabled={shaLoading}
|
|
1156
|
+
onRequestClose={() => setModalType(null)}
|
|
1157
|
+
onRequestSubmit={applySHAClaim}
|
|
1158
|
+
>
|
|
1159
|
+
<Stack gap={4}>
|
|
1160
|
+
{/* ===== Info ===== */}
|
|
1161
|
+
<Tile>
|
|
1162
|
+
<p style={{ marginBottom: '0.5rem' }}>
|
|
1163
|
+
You are about to submit the following items for <strong>{schemes} claim</strong>.
|
|
1164
|
+
</p>
|
|
1165
|
+
<p style={{ color: '#6f6f6f', marginBottom: 0 }}>
|
|
1166
|
+
Please confirm all services are correct before proceeding.
|
|
1167
|
+
</p>
|
|
1168
|
+
</Tile>
|
|
1169
|
+
|
|
1170
|
+
{/* ===== Items Table ===== */}
|
|
1171
|
+
<Tile>
|
|
1172
|
+
<h5 style={{ marginBottom: '1rem' }}>Claim Items</h5>
|
|
1173
|
+
|
|
1174
|
+
<DataTable
|
|
1175
|
+
rows={pendingShaItems.map((i) => ({
|
|
1176
|
+
id: i.id,
|
|
1177
|
+
service: i.service,
|
|
1178
|
+
quantity: i.quantity,
|
|
1179
|
+
unitPrice: `Ksh ${Number(i.price).toLocaleString()}`,
|
|
1180
|
+
total: `Ksh ${getBalance(i).toLocaleString()}`,
|
|
1181
|
+
}))}
|
|
1182
|
+
headers={[
|
|
1183
|
+
{ key: 'service', header: 'Service' },
|
|
1184
|
+
{ key: 'quantity', header: 'Qty' },
|
|
1185
|
+
{ key: 'unitPrice', header: 'Unit Price' },
|
|
1186
|
+
{ key: 'total', header: 'Total' },
|
|
1187
|
+
]}
|
|
1188
|
+
>
|
|
1189
|
+
{({ rows, headers, getHeaderProps, getRowProps }) => (
|
|
1190
|
+
<Table size="sm" useZebraStyles>
|
|
1191
|
+
<TableHead>
|
|
1192
|
+
<TableRow>
|
|
1193
|
+
{headers.map((h) => (
|
|
1194
|
+
<TableHeader key={h.key} {...getHeaderProps({ header: h })}>
|
|
1195
|
+
{h.header}
|
|
1196
|
+
</TableHeader>
|
|
1197
|
+
))}
|
|
1198
|
+
</TableRow>
|
|
1199
|
+
</TableHead>
|
|
1200
|
+
|
|
1201
|
+
<TableBody>
|
|
1202
|
+
{rows.map((row) => (
|
|
1203
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
1204
|
+
{row.cells.map((cell) => (
|
|
1205
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
1206
|
+
))}
|
|
1207
|
+
</TableRow>
|
|
1208
|
+
))}
|
|
1209
|
+
</TableBody>
|
|
1210
|
+
</Table>
|
|
1211
|
+
)}
|
|
1212
|
+
</DataTable>
|
|
1213
|
+
</Tile>
|
|
1214
|
+
|
|
1215
|
+
{/* ===== Summary ===== */}
|
|
1216
|
+
<Tile>
|
|
1217
|
+
<Grid fullWidth>
|
|
1218
|
+
<Column lg={8}>
|
|
1219
|
+
<p>
|
|
1220
|
+
<strong>Items:</strong> {pendingShaItems.length}
|
|
1221
|
+
</p>
|
|
1222
|
+
</Column>
|
|
1223
|
+
|
|
1224
|
+
<Column lg={8} style={{ textAlign: 'right' }}>
|
|
1225
|
+
<p style={{ fontSize: '1.1rem', fontWeight: 600 }}>
|
|
1226
|
+
Total: Ksh {pendingShaItems.reduce((acc, i) => acc + getBalance(i), 0).toLocaleString()}
|
|
1227
|
+
</p>
|
|
1228
|
+
</Column>
|
|
1229
|
+
</Grid>
|
|
1230
|
+
</Tile>
|
|
1231
|
+
</Stack>
|
|
1232
|
+
</Modal>
|
|
1233
|
+
|
|
1234
|
+
{/* claim status */}
|
|
1235
|
+
<Modal
|
|
1236
|
+
open={modalType === 'CHECK_SHA'}
|
|
1237
|
+
modalHeading="SHA Claim Status"
|
|
1238
|
+
primaryButtonText="Close"
|
|
1239
|
+
secondaryButtonText={null}
|
|
1240
|
+
onRequestClose={() => setModalType(null)}
|
|
1241
|
+
onRequestSubmit={() => setModalType(null)}
|
|
1242
|
+
>
|
|
1243
|
+
{claimResponse ? (
|
|
1244
|
+
<Stack gap={4}>
|
|
1245
|
+
<Tile>
|
|
1246
|
+
<h5 style={{ marginBottom: '0.5rem' }}>Patient Information</h5>
|
|
1247
|
+
<p>
|
|
1248
|
+
<strong>Name:</strong> {claimResponse.patientFullName}
|
|
1249
|
+
</p>
|
|
1250
|
+
<p>
|
|
1251
|
+
<strong>Gender:</strong> {claimResponse.gender}
|
|
1252
|
+
</p>
|
|
1253
|
+
|
|
1254
|
+
<h5 style={{ marginTop: '1rem', marginBottom: '0.5rem' }}>Facility</h5>
|
|
1255
|
+
<p>
|
|
1256
|
+
{claimResponse.facilityName} ({claimResponse.facilityLevel})
|
|
1257
|
+
</p>
|
|
1258
|
+
</Tile>
|
|
1259
|
+
|
|
1260
|
+
<Tile>
|
|
1261
|
+
<h5 style={{ marginBottom: '0.5rem' }}>Claim Details</h5>
|
|
1262
|
+
|
|
1263
|
+
<p>
|
|
1264
|
+
<strong>Status:</strong>{' '}
|
|
1265
|
+
<Tag type={getClaimTagType(claimResponse.claim_Status)}>{claimResponse.claim_Status}</Tag>
|
|
1266
|
+
</p>
|
|
1267
|
+
|
|
1268
|
+
<p>
|
|
1269
|
+
<strong>Response:</strong> {claimResponse.claim_Response || '-'}
|
|
1270
|
+
</p>
|
|
1271
|
+
</Tile>
|
|
1272
|
+
|
|
1273
|
+
{claimResponse.services?.length > 0 && (
|
|
1274
|
+
<Tile>
|
|
1275
|
+
<h5 style={{ marginBottom: '1rem' }}>Services</h5>
|
|
1276
|
+
|
|
1277
|
+
<DataTable
|
|
1278
|
+
rows={claimResponse.services.map((s: any) => ({
|
|
1279
|
+
id: s.serviceCode,
|
|
1280
|
+
name: s.serviceDisplay,
|
|
1281
|
+
code: s.serviceCode,
|
|
1282
|
+
quantity: s.quantity,
|
|
1283
|
+
amount: `Ksh ${Number(s.totalAmount).toLocaleString()}`,
|
|
1284
|
+
}))}
|
|
1285
|
+
headers={[
|
|
1286
|
+
{ key: 'name', header: 'Service' },
|
|
1287
|
+
{ key: 'code', header: 'Code' },
|
|
1288
|
+
{ key: 'quantity', header: 'Qty' },
|
|
1289
|
+
{ key: 'amount', header: 'Total Amount' },
|
|
1290
|
+
]}
|
|
1291
|
+
>
|
|
1292
|
+
{({ rows, headers, getHeaderProps, getRowProps }) => (
|
|
1293
|
+
<Table size="sm">
|
|
1294
|
+
<TableHead>
|
|
1295
|
+
<TableRow>
|
|
1296
|
+
{headers.map((h) => (
|
|
1297
|
+
<TableHeader key={h.key} {...getHeaderProps({ header: h })}>
|
|
1298
|
+
{h.header}
|
|
1299
|
+
</TableHeader>
|
|
1300
|
+
))}
|
|
1301
|
+
</TableRow>
|
|
1302
|
+
</TableHead>
|
|
1303
|
+
|
|
1304
|
+
<TableBody>
|
|
1305
|
+
{rows.map((row) => (
|
|
1306
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
1307
|
+
{row.cells.map((cell) => (
|
|
1308
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
1309
|
+
))}
|
|
1310
|
+
</TableRow>
|
|
1311
|
+
))}
|
|
1312
|
+
</TableBody>
|
|
1313
|
+
</Table>
|
|
1314
|
+
)}
|
|
1315
|
+
</DataTable>
|
|
1316
|
+
</Tile>
|
|
1317
|
+
)}
|
|
1318
|
+
|
|
1319
|
+
{claimResponse.diagnoses?.length > 0 && (
|
|
1320
|
+
<Tile>
|
|
1321
|
+
<h5 style={{ marginBottom: '1rem' }}>Diagnoses</h5>
|
|
1322
|
+
|
|
1323
|
+
<DataTable
|
|
1324
|
+
rows={claimResponse.diagnoses.map((d: any, index: number) => ({
|
|
1325
|
+
id: `${d.Code}-${index}`,
|
|
1326
|
+
name: d.Display,
|
|
1327
|
+
code: d.Code,
|
|
1328
|
+
}))}
|
|
1329
|
+
headers={[
|
|
1330
|
+
{ key: 'name', header: 'Diagnosis' },
|
|
1331
|
+
{ key: 'code', header: 'Code' },
|
|
1332
|
+
]}
|
|
1333
|
+
>
|
|
1334
|
+
{({ rows, headers, getHeaderProps, getRowProps }) => (
|
|
1335
|
+
<Table size="sm">
|
|
1336
|
+
<TableHead>
|
|
1337
|
+
<TableRow>
|
|
1338
|
+
{headers.map((h) => (
|
|
1339
|
+
<TableHeader key={h.key} {...getHeaderProps({ header: h })}>
|
|
1340
|
+
{h.header}
|
|
1341
|
+
</TableHeader>
|
|
1342
|
+
))}
|
|
1343
|
+
</TableRow>
|
|
1344
|
+
</TableHead>
|
|
1345
|
+
|
|
1346
|
+
<TableBody>
|
|
1347
|
+
{rows.map((row) => (
|
|
1348
|
+
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
1349
|
+
{row.cells.map((cell) => (
|
|
1350
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
1351
|
+
))}
|
|
1352
|
+
</TableRow>
|
|
1353
|
+
))}
|
|
1354
|
+
</TableBody>
|
|
1355
|
+
</Table>
|
|
1356
|
+
)}
|
|
1357
|
+
</DataTable>
|
|
1358
|
+
</Tile>
|
|
1359
|
+
)}
|
|
1360
|
+
</Stack>
|
|
1361
|
+
) : (
|
|
1362
|
+
<Tile>
|
|
1363
|
+
<p>No claim data available.</p>
|
|
1364
|
+
</Tile>
|
|
1365
|
+
)}
|
|
1366
|
+
</Modal>
|
|
1367
|
+
</Grid>
|
|
1368
|
+
);
|
|
1369
|
+
};
|
|
1370
|
+
|
|
1371
|
+
export default BillDetails;
|