@digihmis/esm-billing-app 1.0.0

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 (115) hide show
  1. package/README.md +4 -0
  2. package/dist/117.js +1 -0
  3. package/dist/117.js.map +1 -0
  4. package/dist/132.js +1 -0
  5. package/dist/132.js.map +1 -0
  6. package/dist/152.js +1 -0
  7. package/dist/152.js.map +1 -0
  8. package/dist/16.js +1 -0
  9. package/dist/16.js.map +1 -0
  10. package/dist/209.js +1 -0
  11. package/dist/209.js.map +1 -0
  12. package/dist/317.js +1 -0
  13. package/dist/317.js.map +1 -0
  14. package/dist/349.js +1 -0
  15. package/dist/349.js.map +1 -0
  16. package/dist/358.js +1 -0
  17. package/dist/358.js.map +1 -0
  18. package/dist/371.js +1 -0
  19. package/dist/371.js.map +1 -0
  20. package/dist/378.js +1 -0
  21. package/dist/378.js.map +1 -0
  22. package/dist/442.js +1 -0
  23. package/dist/442.js.map +1 -0
  24. package/dist/45.js +1 -0
  25. package/dist/45.js.map +1 -0
  26. package/dist/466.js +1 -0
  27. package/dist/466.js.map +1 -0
  28. package/dist/496.js +12 -0
  29. package/dist/496.js.map +1 -0
  30. package/dist/508.js +1 -0
  31. package/dist/508.js.map +1 -0
  32. package/dist/61.js +1 -0
  33. package/dist/61.js.map +1 -0
  34. package/dist/640.js +1 -0
  35. package/dist/640.js.map +1 -0
  36. package/dist/689.js +1 -0
  37. package/dist/689.js.map +1 -0
  38. package/dist/709.js +1 -0
  39. package/dist/709.js.map +1 -0
  40. package/dist/712.js +1 -0
  41. package/dist/712.js.map +1 -0
  42. package/dist/720.js +1 -0
  43. package/dist/720.js.map +1 -0
  44. package/dist/742.js +1 -0
  45. package/dist/742.js.map +1 -0
  46. package/dist/771.js +1 -0
  47. package/dist/771.js.map +1 -0
  48. package/dist/806.js +1 -0
  49. package/dist/806.js.map +1 -0
  50. package/dist/839.js +1 -0
  51. package/dist/839.js.map +1 -0
  52. package/dist/912.js +1 -0
  53. package/dist/912.js.map +1 -0
  54. package/dist/913.js +1 -0
  55. package/dist/913.js.map +1 -0
  56. package/dist/940.js +1 -0
  57. package/dist/940.js.map +1 -0
  58. package/dist/965.js +15 -0
  59. package/dist/965.js.map +1 -0
  60. package/dist/digihmis-esm-billing-app.js +6 -0
  61. package/dist/digihmis-esm-billing-app.js.buildmanifest.json +827 -0
  62. package/dist/digihmis-esm-billing-app.js.map +1 -0
  63. package/dist/main.js +17 -0
  64. package/dist/main.js.map +1 -0
  65. package/dist/routes.json +1 -0
  66. package/jest.config.js +26 -0
  67. package/package.json +51 -0
  68. package/rspack.config.js +1 -0
  69. package/src/billing-checkin-form/billing-checkin-form-schema/index.ts +11 -0
  70. package/src/billing-checkin-form/billing-checkin-form.extension.tsx +179 -0
  71. package/src/billing-checkin-form/billing-checkin-form.resource.ts +42 -0
  72. package/src/billing-checkin-form/billing-checkin-form.scss +82 -0
  73. package/src/billing-checkin-form/billing-patient-type-tag/billing-patient-type-tag.component.tsx +39 -0
  74. package/src/billing-checkin-form/billing-patient-type-tag/billing-patient-type-tag.resource.ts +57 -0
  75. package/src/billing-checkin-form/billing-patient-type-tag/billing-patient-type-tag.scss +4 -0
  76. package/src/billing-checkin-form/helper/index.ts +2 -0
  77. package/src/billing-checkin-form/type/index.ts +42 -0
  78. package/src/billing-checkin-form/visit-attributes/visit-attributes-form.component.tsx +244 -0
  79. package/src/billing-checkin-form/visit-attributes/visit-attributes-form.resource.ts +23 -0
  80. package/src/billing-checkin-form/visit-attributes/visit-attributes-form.scss +186 -0
  81. package/src/billing-dashboard/billing-dashboard.component.tsx +224 -0
  82. package/src/billing-dashboard/billing-dashboard.resource.ts +101 -0
  83. package/src/billing-dashboard/billing-dashboard.scss +166 -0
  84. package/src/billing-dashboard/metrics/metrics.resource.ts +142 -0
  85. package/src/billing-dashboard/type/index.ts +176 -0
  86. package/src/billing-header/page-header/page-header.component.tsx +35 -0
  87. package/src/billing-header/page-header/page-header.scss +76 -0
  88. package/src/billing-header/page-header/page-icon/icon-wrapper.extension.tsx +17 -0
  89. package/src/billing-timesheet-modal/billing-timesheet-modal.scss +56 -0
  90. package/src/billing-timesheet-modal/billing-timesheet-modal.tsx +281 -0
  91. package/src/billing-timesheet-modal/billing-timesheet-resource-modal.ts +37 -0
  92. package/src/bills-table/bills-table.component.tsx +323 -0
  93. package/src/bills-table/bills-table.scss +252 -0
  94. package/src/config-schema.ts +148 -0
  95. package/src/constants/index.ts +2 -0
  96. package/src/dashboard.meta.ts +16 -0
  97. package/src/declarations.d.ts +6 -0
  98. package/src/index.ts +37 -0
  99. package/src/leftpanel/left-panel.component.tsx +98 -0
  100. package/src/leftpanel/left-panel.scss +13 -0
  101. package/src/routes.json +58 -0
  102. package/src/setup-tests.ts +1 -0
  103. package/src/utils/index.ts +270 -0
  104. package/translations/am.json +18 -0
  105. package/translations/ar.json +18 -0
  106. package/translations/en.json +18 -0
  107. package/translations/es.json +18 -0
  108. package/translations/fr.json +18 -0
  109. package/translations/he.json +18 -0
  110. package/translations/km.json +18 -0
  111. package/translations/pt.json +18 -0
  112. package/translations/sw.json +18 -0
  113. package/translations/vi.json +18 -0
  114. package/translations/zh.json +18 -0
  115. package/tsconfig.json +5 -0
@@ -0,0 +1,101 @@
1
+ import { openmrsFetch, restBaseUrl, useSession } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+ import { CashPoint, Timesheet } from './type';
4
+
5
+ /**
6
+ * Hook to get the current cashier's active timesheet (clocked-in session)
7
+ */
8
+ export const useCurrentTimesheet = () => {
9
+ const session = useSession();
10
+ const providerUuid = session?.currentProvider?.uuid;
11
+
12
+ const { data, error, isLoading, mutate } = useSWR<{ data: { results: Array<Timesheet> } }>(
13
+ providerUuid ? `${restBaseUrl}/cashier/timesheet?cashier=${providerUuid}&getProviderOpenTimesheet=true` : null,
14
+ openmrsFetch,
15
+ );
16
+
17
+ const activeTimesheet = data?.data?.results?.[0] || null;
18
+ const isClockedIn = activeTimesheet !== null && activeTimesheet.clockOut === null;
19
+
20
+ return {
21
+ activeTimesheet,
22
+ isClockedIn,
23
+ isLoading,
24
+ error,
25
+ mutate,
26
+ };
27
+ };
28
+
29
+ /**
30
+ * Get all timesheets for a cashier within a date range
31
+ */
32
+ export const useTimesheets = (
33
+ cashierUuid?: string,
34
+ cashPointUuid?: string,
35
+ startDatetime?: Date,
36
+ endDatetime?: Date,
37
+ ) => {
38
+ const params = new URLSearchParams();
39
+
40
+ if (cashierUuid) {
41
+ params.append('cashier', cashierUuid);
42
+ }
43
+ if (cashPointUuid) {
44
+ params.append('cashPoint', cashPointUuid);
45
+ }
46
+ if (startDatetime) {
47
+ params.append('startDatetime', startDatetime.toISOString());
48
+ }
49
+ if (endDatetime) {
50
+ params.append('endDatetime', endDatetime.toISOString());
51
+ }
52
+
53
+ const url = `${restBaseUrl}/cashier/timesheet?${params.toString()}`;
54
+
55
+ const { data, error, isLoading, mutate } = useSWR<{ data: { results: Array<Timesheet> } }>(
56
+ cashierUuid || cashPointUuid || startDatetime || endDatetime ? url : null,
57
+ openmrsFetch,
58
+ );
59
+
60
+ return {
61
+ timesheets: data?.data?.results || [],
62
+ error,
63
+ isLoading,
64
+ mutate,
65
+ };
66
+ };
67
+
68
+ /**
69
+ * Hook to fetch all available cash points
70
+ */
71
+ export const useCashPoints = () => {
72
+ const url = `${restBaseUrl}/cashier/cashPoint?v=default`;
73
+
74
+ const { data, error, isLoading, mutate } = useSWR<{ data: { results: Array<CashPoint> } }>(url, openmrsFetch);
75
+
76
+ // Filter out retired cash points
77
+ const activeCashPoints = data?.data?.results?.filter((cp) => !cp.retired) || [];
78
+
79
+ return {
80
+ cashPoints: activeCashPoints,
81
+ error,
82
+ isLoading,
83
+ mutate,
84
+ };
85
+ };
86
+
87
+ /**
88
+ * Get a specific cash point by UUID
89
+ */
90
+ export const useCashPoint = (cashPointUuid: string) => {
91
+ const url = cashPointUuid ? `${restBaseUrl}/cashier/cashPoint/${cashPointUuid}?v=default` : null;
92
+
93
+ const { data, error, isLoading, mutate } = useSWR<{ data: CashPoint }>(url, openmrsFetch);
94
+
95
+ return {
96
+ cashPoint: data?.data || null,
97
+ error,
98
+ isLoading,
99
+ mutate,
100
+ };
101
+ };
@@ -0,0 +1,166 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+ @use '@openmrs/esm-styleguide/src/vars' as *;
5
+
6
+ .tileContainer {
7
+ border: 1px solid colors.$cool-gray-30;
8
+ margin-bottom: layout.$spacing-04;
9
+ display: grid;
10
+ grid-template-columns: 1fr 1fr 1fr 1fr;
11
+ gap: 0;
12
+ height: 80px;
13
+
14
+ :global(.omrs-breakpoint-lt-desktop) & {
15
+ padding: layout.$spacing-04;
16
+ }
17
+
18
+ :global(.omrs-breakpoint-lt-tablet) & {
19
+ padding: layout.$spacing-03;
20
+ grid-template-columns: 1fr;
21
+ grid-template-rows: auto auto auto;
22
+ gap: layout.$spacing-04;
23
+ }
24
+ }
25
+
26
+ .tileHeader {
27
+ color: colors.$gray-70;
28
+ margin-bottom: layout.$spacing-03;
29
+ text-transform: none;
30
+ letter-spacing: 0;
31
+
32
+ :global(.omrs-breakpoint-lt-tablet) & {
33
+ margin-bottom: layout.$spacing-02;
34
+ }
35
+ }
36
+
37
+ .displayDetails {
38
+ display: flex;
39
+ flex-direction: column;
40
+ align-items: flex-start;
41
+
42
+ :global(.omrs-breakpoint-lt-tablet) & {
43
+ padding: layout.$spacing-03 0;
44
+ border-right: none;
45
+ border-bottom: 1px solid colors.$gray-20;
46
+ padding-bottom: layout.$spacing-04;
47
+ margin-bottom: layout.$spacing-03;
48
+ }
49
+ }
50
+
51
+ .countLabel {
52
+ color: colors.$gray-70;
53
+ margin-bottom: layout.$spacing-02;
54
+ @include type.type-style('caption-01');
55
+ font-weight: bold;
56
+
57
+ :global(.omrs-breakpoint-lt-tablet) & {
58
+ @include type.type-style('caption-01');
59
+ font-weight: bold;
60
+ margin-bottom: layout.$spacing-01;
61
+ }
62
+ }
63
+
64
+ .displayData {
65
+ color: colors.$gray-100;
66
+ font-weight: bold;
67
+ @include type.type-style('caption-01');
68
+ font-size: 25px;
69
+
70
+ :global(.omrs-breakpoint-lt-tablet) & {
71
+ font-size: 2.625rem;
72
+ line-height: 2.75rem;
73
+ }
74
+ }
75
+
76
+ .headerActions {
77
+ display: flex;
78
+ align-items: center;
79
+ justify-content: center;
80
+ padding-right: 1rem;
81
+
82
+ :global(.omrs-breakpoint-lt-tablet) & {
83
+ gap: 0.25rem;
84
+ }
85
+ }
86
+
87
+ .dashboardContainer {
88
+ padding: layout.$spacing-05;
89
+ padding-bottom: 0;
90
+ background-color: var(--cds-field);
91
+
92
+ :global(.omrs-breakpoint-lt-desktop) & {
93
+ padding: layout.$spacing-02;
94
+ padding-bottom: 0;
95
+ }
96
+
97
+ :global(.omrs-breakpoint-lt-tablet) & {
98
+ padding: layout.$spacing-03;
99
+ padding-bottom: 0;
100
+ }
101
+ }
102
+
103
+ .clockInInfo {
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: flex-end;
107
+ width: 100%;
108
+ gap: layout.$spacing-03;
109
+
110
+ :global(.omrs-breakpoint-lt-tablet) & {
111
+ flex-direction: row;
112
+ align-items: center;
113
+ gap: layout.$spacing-02;
114
+ }
115
+ }
116
+
117
+ .clockInText {
118
+ display: flex;
119
+ align-items: center;
120
+ flex-wrap: wrap;
121
+ gap: 0.25rem;
122
+
123
+ :global(.omrs-breakpoint-lt-tablet) & {
124
+ font-size: 0.875rem;
125
+ }
126
+ }
127
+
128
+ .clockInTime {
129
+ color: colors.$gray-70;
130
+ white-space: nowrap;
131
+ }
132
+
133
+ .alarmIcon {
134
+ flex-shrink: 0;
135
+
136
+ :global(.omrs-breakpoint-lt-tablet) & {
137
+ width: 18px;
138
+ height: 18px;
139
+ }
140
+ }
141
+
142
+ .emptyStateContainer {
143
+ border: 1px solid colors.$gray-20;
144
+ padding: 3rem 0;
145
+ }
146
+
147
+ .tile {
148
+ margin: auto;
149
+ width: fit-content;
150
+ background: colors.$gray-20;
151
+ }
152
+
153
+ .tileContent {
154
+ display: flex;
155
+ flex-direction: column;
156
+ align-items: center;
157
+ }
158
+
159
+ .content {
160
+ @include type.type-style('heading-compact-02');
161
+ color: colors.$gray-70;
162
+ margin-bottom: layout.$spacing-03;
163
+ }
164
+ .loader {
165
+ min-height: fit-content;
166
+ }
@@ -0,0 +1,142 @@
1
+ import dayjs from 'dayjs';
2
+ import { type MappedBill, type PatientInvoice, PaymentStatus } from '../type';
3
+ import { formatDate, openmrsFetch, parseDate, restBaseUrl } from '@openmrs/esm-framework';
4
+ import sortBy from 'lodash-es/sortBy';
5
+ import useSWR from 'swr';
6
+ import isEmpty from 'lodash-es/isEmpty';
7
+ import { extractString, useCurrencyFormatting } from '../../utils';
8
+
9
+ export const mapBillProperties = (bill: PatientInvoice): MappedBill => {
10
+ const mappedBill: MappedBill = {
11
+ id: bill?.id,
12
+ uuid: bill?.uuid,
13
+ patientName: bill?.patient?.display.split('-')?.[1],
14
+ identifier: bill?.patient?.display.split('-')?.[0],
15
+ patientUuid: bill?.patient?.uuid,
16
+ status: bill?.lineItems.some((item) => item?.paymentStatus === PaymentStatus.PENDING)
17
+ ? PaymentStatus.PENDING
18
+ : PaymentStatus.PAID,
19
+ receiptNumber: bill?.receiptNumber,
20
+ cashier: bill?.cashier,
21
+ cashPointUuid: bill?.cashPoint?.uuid,
22
+ cashPointName: bill?.cashPoint?.name,
23
+ cashPointLocation: bill?.cashPoint?.location?.display,
24
+ dateCreated: bill?.dateCreated ? formatDate(parseDate(bill?.dateCreated), { mode: 'wide' }) : '--',
25
+ dateCreatedUnformatted: bill?.dateCreated,
26
+ lineItems: bill?.lineItems.filter((li) => !li?.voided),
27
+ billingService: extractString(
28
+ bill?.lineItems.map((bill) => bill?.item || bill?.billableService || '--').join(' '),
29
+ ),
30
+ payments: bill?.payments,
31
+ display: bill?.display,
32
+ totalAmount: bill?.lineItems?.map((item) => item?.price * item?.quantity).reduce((prev, curr) => prev + curr, 0),
33
+ tenderedAmount: bill?.payments?.map((item) => item?.amountTendered).reduce((prev, curr) => prev + curr, 0),
34
+ referenceCodes: bill?.payments
35
+ .map((payment) =>
36
+ payment.attributes
37
+ .filter((attr) => attr.attributeType.description === 'Reference Number')
38
+ .map((attr) => {
39
+ return {
40
+ paymentMode: payment.instanceType.name,
41
+ value: attr.value,
42
+ };
43
+ }),
44
+ )
45
+ .flat()
46
+ .map((ref) => `${ref.paymentMode}: ${ref.value}`)
47
+ .join(', '),
48
+ adjustmentReason: bill?.adjustmentReason,
49
+ balance: bill?.balance,
50
+ totalPayments: bill?.totalPayments,
51
+ totalDeposits: bill?.totalDeposits,
52
+ totalExempted: bill?.totalExempted,
53
+ closed: bill?.closed,
54
+ };
55
+
56
+ return mappedBill;
57
+ };
58
+ export const useBills = (
59
+ patientUuid: string = '',
60
+ billStatus: PaymentStatus.PENDING | '' | string = '',
61
+ startingDate: Date = dayjs().startOf('day').toDate(),
62
+ endDate: Date = dayjs().endOf('day').toDate(),
63
+ ) => {
64
+ const startingDateISO = startingDate.toISOString();
65
+ const endDateISO = endDate.toISOString();
66
+
67
+ const url = `${restBaseUrl}/cashier/bill?status=${billStatus}&v=custom:(uuid,display,voided,voidReason,adjustedBy,cashPoint:(uuid,name),cashier:(uuid,display),dateCreated,lineItems,patient:(uuid,display))&createdOnOrAfter=${startingDateISO}&createdOnOrBefore=${endDateISO}`;
68
+
69
+ const { data, error, isLoading, isValidating, mutate } = useSWR<{ data: { results: Array<PatientInvoice> } }>(
70
+ patientUuid ? `${url}&patientUuid=${patientUuid}` : url,
71
+ openmrsFetch,
72
+ {
73
+ errorRetryCount: 2,
74
+ },
75
+ );
76
+
77
+ const sortBills = sortBy(data?.data?.results ?? [], ['dateCreated']).reverse();
78
+ const filteredBills = billStatus === '' ? sortBills : sortBills?.filter((bill) => bill?.status === billStatus);
79
+ const mappedResults = filteredBills?.map((bill) => mapBillProperties(bill));
80
+ const filteredResults = mappedResults?.filter((res) => res.patientUuid === patientUuid);
81
+ const formattedBills = isEmpty(patientUuid) ? mappedResults : filteredResults || [];
82
+
83
+ return {
84
+ bills: formattedBills,
85
+ error,
86
+ isLoading,
87
+ isValidating,
88
+ mutate,
89
+ };
90
+ };
91
+
92
+ /**
93
+ * A custom hook for calculating bill metrics.
94
+ *
95
+ * This hook takes in an array of bills and calculates the total amount for different
96
+ * bill statuses (cumulative, pending, paid) using provided helper functions.
97
+ *
98
+ * @param {Array<Object>} bills - An array of bill objects. Each bill object should have a `status` and `lineItems` properties.
99
+ *
100
+ */
101
+
102
+ export const useBillMetrics = (
103
+ bills: Array<MappedBill>,
104
+ ): {
105
+ totalBills: string;
106
+ pendingBills: string;
107
+ paidBills: string;
108
+ exemptedBills: string;
109
+ } => {
110
+ const { paidTotal, pendingTotal, cumulativeTotal, exemptedTotal } = calculateBillTotals(bills);
111
+ const { format: formatCurrency } = useCurrencyFormatting();
112
+
113
+ return {
114
+ totalBills: formatCurrency(cumulativeTotal),
115
+ pendingBills: formatCurrency(pendingTotal),
116
+ paidBills: formatCurrency(paidTotal),
117
+ exemptedBills: formatCurrency(exemptedTotal),
118
+ };
119
+ };
120
+
121
+ const calculateBillTotals = (bills: Array<MappedBill>) => {
122
+ let paidTotal = 0;
123
+ let pendingTotal = 0;
124
+ let cumulativeTotal = 0;
125
+ let exemptedTotal = 0;
126
+
127
+ bills.forEach((bill) => {
128
+ const amount = bill.totalAmount || 0;
129
+ if (bill.status === PaymentStatus.PAID) {
130
+ paidTotal += amount;
131
+ } else if (bill.status === PaymentStatus.PENDING) {
132
+ pendingTotal += amount;
133
+ } else if (bill.status === PaymentStatus.EXEMPTED) {
134
+ exemptedTotal += amount;
135
+ }
136
+ cumulativeTotal += amount;
137
+ });
138
+
139
+ return { paidTotal, pendingTotal, cumulativeTotal, exemptedTotal };
140
+ };
141
+
142
+ export default calculateBillTotals;
@@ -0,0 +1,176 @@
1
+ import { OpenmrsResource } from '@openmrs/esm-framework/';
2
+
3
+ export enum PaymentStatus {
4
+ POSTED = 'POSTED',
5
+ PENDING = 'PENDING',
6
+ PAID = 'PAID',
7
+ CREDITED = 'CREDITED',
8
+ CANCELLED = 'CANCELLED',
9
+ ADJUSTED = 'ADJUSTED',
10
+ EXEMPTED = 'EXEMPTED',
11
+ }
12
+ export interface Provider {
13
+ uuid: string;
14
+ display: string;
15
+ }
16
+
17
+ export interface LineItem {
18
+ uuid: string;
19
+ display: string;
20
+ voided: boolean;
21
+ voidReason: string | null;
22
+ item: string;
23
+ billableService: string;
24
+ quantity: number;
25
+ price: number;
26
+ priceName: string;
27
+ priceUuid: string;
28
+ lineItemOrder: number;
29
+ resourceVersion: string;
30
+ paymentStatus: string;
31
+ itemOrServiceConceptUuid: string;
32
+ serviceTypeUuid: string;
33
+ order: OpenmrsResource;
34
+ }
35
+ interface AttributeType {
36
+ uuid?: string;
37
+ name: string;
38
+ description: string;
39
+ retired: boolean;
40
+ attributeOrder?: number;
41
+ format?: string;
42
+ foreignKey?: string | null;
43
+ regExp?: string | null;
44
+ required: boolean;
45
+ value?: string;
46
+ }
47
+
48
+ interface Attribute {
49
+ uuid: string;
50
+ display: string;
51
+ voided: boolean;
52
+ voidReason: string | null;
53
+ value: string;
54
+ attributeType: AttributeType;
55
+ order: number;
56
+ valueName: string;
57
+ resourceVersion: string;
58
+ }
59
+ interface PaymentInstanceType {
60
+ uuid: string;
61
+ name: string;
62
+ description: string;
63
+ retired: boolean;
64
+ }
65
+ export interface Payment {
66
+ uuid: string;
67
+ instanceType: PaymentInstanceType;
68
+ attributes: Array<Attribute>;
69
+ amount: number;
70
+ amountTendered: number;
71
+ dateCreated: number;
72
+ voided: boolean;
73
+ resourceVersion: string;
74
+ }
75
+ export interface MappedBill {
76
+ uuid: string;
77
+ id: number;
78
+ patientUuid: string;
79
+ patientName: string;
80
+ cashPointUuid: string;
81
+ cashPointName: string;
82
+ cashPointLocation: string;
83
+ cashier: Provider;
84
+ receiptNumber: string;
85
+ status: PaymentStatus;
86
+ identifier: string;
87
+ dateCreated: string;
88
+ dateCreatedUnformatted: string;
89
+ lineItems: Array<LineItem>;
90
+ billingService: string;
91
+ payments: Array<Payment>;
92
+ totalAmount?: number;
93
+ tenderedAmount?: number;
94
+ display?: string;
95
+ referenceCodes?: string;
96
+ adjustmentReason?: string;
97
+ totalPayments?: number;
98
+ totalDeposits?: number;
99
+ totalExempted?: number;
100
+ balance?: number;
101
+ closed?: boolean;
102
+ }
103
+ interface LocationLink {
104
+ rel: string;
105
+ uri: string;
106
+ resourceAlias: string;
107
+ }
108
+
109
+ interface Location {
110
+ uuid: string;
111
+ display: string;
112
+ links: Array<LocationLink>;
113
+ }
114
+ export interface CashPoint {
115
+ uuid: string;
116
+ name: string;
117
+ description: string;
118
+ retired: boolean;
119
+ location: Location;
120
+ }
121
+
122
+ interface PatientLink {
123
+ rel: string;
124
+ uri: string;
125
+ resourceAlias: string;
126
+ }
127
+
128
+ export interface Patient {
129
+ uuid: string;
130
+ display: string;
131
+ links: Array<PatientLink>;
132
+ identifiers: Array<{ uuid: string; display: string }>;
133
+ }
134
+ export interface PatientInvoice {
135
+ uuid: string;
136
+ display: string;
137
+ voided: boolean;
138
+ voidReason: string | null;
139
+ adjustedBy: any[];
140
+ billAdjusted: any;
141
+ cashPoint: CashPoint;
142
+ cashier: Provider;
143
+ dateCreated: string;
144
+ lineItems: Array<LineItem>;
145
+ patient: Patient;
146
+ payments: Payment[];
147
+ receiptNumber: string;
148
+ status: PaymentStatus;
149
+ adjustmentReason: any;
150
+ id: number;
151
+ resourceVersion: string;
152
+ totalPayments?: number;
153
+ totalDeposits?: number;
154
+ totalExempted?: number;
155
+ balance?: number;
156
+ closed?: boolean;
157
+ }
158
+
159
+ export interface Timesheet {
160
+ uuid: string;
161
+ cashier: {
162
+ uuid: string;
163
+ display: string;
164
+ };
165
+ cashPoint: {
166
+ uuid: string;
167
+ name: string;
168
+ location?: {
169
+ uuid: string;
170
+ display: string;
171
+ };
172
+ };
173
+ clockIn: string;
174
+ clockOut: string | null;
175
+ display: string;
176
+ }
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Calendar } from '@carbon/react/icons';
4
+ import { useSession, formatDate, ExtensionSlot, useConfig } from '@openmrs/esm-framework';
5
+ import styles from './page-header.scss';
6
+ import { IconWrapper } from './page-icon/icon-wrapper.extension';
7
+ import { BillingConfig } from '../../config-schema';
8
+
9
+ export const PageHeader: React.FC = () => {
10
+ const { t } = useTranslation();
11
+ const userSession = useSession();
12
+ const { billingIcon } = useConfig<BillingConfig>();
13
+ const userLocation = userSession?.sessionLocation?.display;
14
+
15
+ return (
16
+ <div className={styles.header}>
17
+ <div className={styles['left-justified-items']}>
18
+ <IconWrapper src={billingIcon.src} alt={billingIcon.alt} />
19
+ <div className={styles['page-labels']}>
20
+ <p className={styles['page-name']}>{userLocation}</p>
21
+ <p className={styles['page-subtitle']}>{t('billingHeader', 'Billing')}</p>
22
+ </div>
23
+ </div>
24
+ <div className={styles['right-justified-items']}>
25
+ <div className={styles.extensionSlot}>
26
+ <ExtensionSlot name={'provider-banner-slot'} />
27
+ <div className={styles['date-and-location']}>
28
+ <Calendar size={16} />
29
+ <span className={styles.value}>{formatDate(new Date(), { mode: 'wide', noToday: true })}</span>
30
+ </div>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ );
35
+ };
@@ -0,0 +1,76 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+
5
+ .header {
6
+ @include type.type-style('body-compact-02');
7
+ height: layout.$spacing-12;
8
+ border: 1px solid colors.$gray-20;
9
+ border-left: 0px;
10
+ display: flex;
11
+ background: var(--cds-field);
12
+ justify-content: space-between;
13
+ margin-top: 1.5rem;
14
+ }
15
+
16
+ .left-justified-items {
17
+ display: flex;
18
+ flex-direction: row;
19
+ align-items: center;
20
+ }
21
+
22
+ .right-justified-items {
23
+ @include type.type-style('body-01');
24
+ color: colors.$gray-80;
25
+ padding-top: layout.$spacing-05;
26
+ padding-bottom: layout.$spacing-07;
27
+ }
28
+
29
+ .page-subtitle {
30
+ @include type.type-style('caption-01');
31
+ color: colors.$gray-70;
32
+ font-size: layout.$spacing-06;
33
+ }
34
+ .page-name {
35
+ @include type.type-style('caption-01');
36
+ color: colors.$gray-80;
37
+ font-size: layout.$spacing-05;
38
+ }
39
+
40
+ .page-labels {
41
+ p:first-of-type {
42
+ margin-bottom: layout.$spacing-02;
43
+ }
44
+ }
45
+
46
+ .date-and-location {
47
+ margin-top: layout.$spacing-03;
48
+ display: flex;
49
+ justify-content: flex-end;
50
+ align-items: center;
51
+ margin-right: layout.$spacing-05;
52
+ @include type.type-style('caption-01');
53
+ font-size: 0.83rem;
54
+ }
55
+
56
+ .value {
57
+ margin-left: layout.$spacing-02;
58
+ }
59
+
60
+ .middot {
61
+ margin: 0 layout.$spacing-03;
62
+ }
63
+
64
+ .view {
65
+ @include type.type-style('caption-01');
66
+ }
67
+
68
+ svg.iconOverrides {
69
+ width: 72 !important;
70
+ height: 72 !important;
71
+ }
72
+
73
+ .svgContainer svg {
74
+ width: 72px;
75
+ height: 72px;
76
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+
3
+ interface IconWrapperProps {
4
+ src: string;
5
+ alt?: string;
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ }
9
+
10
+ export const IconWrapper: React.FC<IconWrapperProps> = ({
11
+ src,
12
+ alt = 'Icon',
13
+ className,
14
+ style = { width: '100px', height: '148px' },
15
+ }) => {
16
+ return <img src={src} alt={alt} className={className} style={style} />;
17
+ };