@openmrs/esm-billing-app 1.1.2-pre.1621 → 1.1.2-pre.1630

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,40 @@
1
+ import { useMemo } from 'react';
1
2
  import useSWR from 'swr';
2
3
  import { type OpenmrsResource, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
3
4
  import { apiBasePath } from '../constants';
4
5
 
6
+ type LastVisitInfo = {
7
+ diffDays: number;
8
+ type: string;
9
+ location: string;
10
+ };
11
+
12
+ export const useLastVisitInfo = (
13
+ patientUuid: string,
14
+ ): { lastVisitInfo: LastVisitInfo | null; isLoading: boolean; error: any } => {
15
+ const url = `${restBaseUrl}/visit?patient=${patientUuid}&v=default&limit=1&sort=desc:startDatetime`;
16
+ const { data, isLoading, error } = useSWR<{ data: { results: Array<any> } }>(patientUuid ? url : null, openmrsFetch);
17
+
18
+ const lastVisitInfo = useMemo((): LastVisitInfo | null => {
19
+ const results = data?.data?.results;
20
+ if (!results?.length) return null;
21
+
22
+ const visit = results[0];
23
+ const visitDate = new Date(visit.startDatetime);
24
+ if (isNaN(visitDate.getTime())) return null;
25
+ const diffTime = Math.abs(Date.now() - visitDate.getTime());
26
+ const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
27
+
28
+ return {
29
+ diffDays,
30
+ type: visit.visitType?.display ?? '',
31
+ location: visit.location?.display ?? '',
32
+ };
33
+ }, [data]);
34
+
35
+ return { lastVisitInfo, isLoading, error };
36
+ };
37
+
5
38
  export const useBillableItems = () => {
6
39
  const url = `${apiBasePath}billableService?v=custom:(uuid,name,shortName,serviceStatus,serviceType:(display),servicePrices:(uuid,name,price,paymentMode))`;
7
40
  const { data, isLoading, error } = useSWR<{ data: { results: Array<OpenmrsResource> } }>(url, openmrsFetch);
@@ -3,7 +3,18 @@ import { z } from 'zod';
3
3
  import { zodResolver } from '@hookform/resolvers/zod';
4
4
  import { Controller, useForm } from 'react-hook-form';
5
5
  import { useTranslation } from 'react-i18next';
6
- import { ComboBox, InlineLoading, RadioButton, RadioButtonGroup, Stack, TextInput } from '@carbon/react';
6
+ import {
7
+ ComboBox,
8
+ InlineLoading,
9
+ RadioButton,
10
+ RadioButtonGroup,
11
+ Stack,
12
+ TextInput,
13
+ Toggletip,
14
+ ToggletipButton,
15
+ ToggletipContent,
16
+ } from '@carbon/react';
17
+ import { Information } from '@carbon/react/icons';
7
18
  import { useConfig } from '@openmrs/esm-framework';
8
19
  import { usePaymentMethods } from '../billing-form.resource';
9
20
  import styles from './visit-attributes-form.scss';
@@ -121,7 +132,19 @@ const VisitAttributesForm: React.FC<VisitAttributesFormProps> = ({ setAttributes
121
132
  render={({ field }) => (
122
133
  <RadioButtonGroup
123
134
  className={styles.radioButtonGroup}
124
- legendText={t('paymentDetails', 'Payment details')}
135
+ legendText={
136
+ <div className={styles.paymentDetailsLegend}>
137
+ {t('paymentDetails', 'Payment details')}
138
+ <Toggletip autoAlign align="bottom">
139
+ <ToggletipButton label={t('showInformation', 'Show information')}>
140
+ <Information />
141
+ </ToggletipButton>
142
+ <ToggletipContent>
143
+ <p>{t('nonPayingInfo', 'Any services rendered to non-paying patients will not be billed')}</p>
144
+ </ToggletipContent>
145
+ </Toggletip>
146
+ </div>
147
+ }
125
148
  name="payment-details"
126
149
  onChange={(selected) => field.onChange(selected)}
127
150
  orientation="vertical">
@@ -19,7 +19,11 @@
19
19
  }
20
20
 
21
21
  .radioButtonGroup {
22
+ overflow: visible;
23
+
22
24
  :global(.cds--radio-button-group) {
25
+ overflow: visible;
26
+
23
27
  :global(.cds--radio-button-wrapper) {
24
28
  margin-bottom: layout.$spacing-03;
25
29
 
@@ -28,8 +32,33 @@
28
32
  }
29
33
  }
30
34
  }
35
+
36
+ :global(fieldset.cds--fieldset) {
37
+ overflow: visible;
38
+ }
31
39
  }
32
40
 
33
41
  .stack {
34
42
  margin-bottom: layout.$spacing-05;
43
+ overflow: visible;
44
+ }
45
+
46
+ .paymentDetailsLegend {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: layout.$spacing-03;
50
+ overflow: visible;
51
+
52
+ :global(.cds--toggletip) {
53
+ overflow: visible;
54
+ }
55
+
56
+ :global(.cds--popover-content) {
57
+ z-index: 9100;
58
+ }
59
+
60
+ :global(.cds--toggletip-content) {
61
+ min-width: 16rem;
62
+ white-space: normal;
63
+ }
35
64
  }
@@ -12,12 +12,13 @@ window.spaBase = '/spa';
12
12
  window.getOpenmrsSpaBase = () => '/openmrs/spa/';
13
13
  window.HTMLElement.prototype.scrollIntoView = jest.fn();
14
14
 
15
- // Mock ResizeObserver for Carbon components that use it (e.g., TextArea)
16
- global.ResizeObserver = jest.fn().mockImplementation(() => ({
17
- observe: jest.fn(),
18
- unobserve: jest.fn(),
19
- disconnect: jest.fn(),
20
- }));
15
+ class ResizeObserver {
16
+ observe() {}
17
+ unobserve() {}
18
+ disconnect() {}
19
+ }
20
+ window.ResizeObserver = ResizeObserver;
21
+ global.ResizeObserver = ResizeObserver;
21
22
 
22
23
  // Suppress single-spa warnings in tests (these are expected when using framework mocks)
23
24
  const originalWarn = console.warn;
@@ -105,6 +105,7 @@
105
105
  "errorLoadingBill": "Error loading bill",
106
106
  "errorLoadingBillableServices": "Error loading billable services",
107
107
  "errorLoadingBillServices": "Error loading bill services",
108
+ "errorLoadingLastVisit": "An error occurred while loading the last visit",
108
109
  "errorLoadingPaymentModes": "Error loading payment modes",
109
110
  "errorPrintingInvoice": "Error printing invoice",
110
111
  "errorProcessingPayment": "Error processing payment",
@@ -132,6 +133,10 @@
132
133
  "itemsAddedToBill": "Items added to bill",
133
134
  "itemsAddedToBillSuccessfully": "Items have been added to the bill successfully",
134
135
  "itemsToBeBilled": "Items to be billed",
136
+ "lastVisitError": "Last visit error",
137
+ "lastVisitInfo": "Last Visit Information",
138
+ "lastVisitMsg_one": "The last visit was a {{type}} {{count}} day ago at {{location}}",
139
+ "lastVisitMsg_other": "The last visit was a {{type}} {{count}} days ago at {{location}}",
135
140
  "lineItemDeleted": "Line item deleted",
136
141
  "lineItemDeleteFailed": "Failed to delete line item",
137
142
  "lineItemDeleteSuccess": "Bill line item deleted successfully",
@@ -159,6 +164,7 @@
159
164
  "noMatchingItemsToDisplay": "No matching items to display",
160
165
  "noMatchingServicesToDisplay": "No matching services to display",
161
166
  "nonPaying": "Non paying",
167
+ "nonPayingInfo": "Any services rendered to non-paying patients will not be billed",
162
168
  "noResultsFor": "No results for {{searchTerm}}",
163
169
  "number": "Number",
164
170
  "ok": "OK",
@@ -237,6 +243,7 @@
237
243
  "serviceTypeRequired": "Service type is required",
238
244
  "shortName": "Short name",
239
245
  "shortNameExceedsLimit": "Short name cannot exceed {{MAX_NAME_LENGTH}} characters",
246
+ "showInformation": "Show information",
240
247
  "status": "Service status",
241
248
  "student": "Student",
242
249
  "submitting": "Submitting",