@openmrs/esm-billing-app 1.0.2-pre.711 → 1.0.2-pre.721
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/4300.js +1 -1
- package/dist/4724.js +1 -1
- package/dist/4724.js.map +1 -1
- package/dist/4739.js +1 -1
- package/dist/4739.js.map +1 -1
- package/dist/7452.js +1 -1
- package/dist/7452.js.map +1 -1
- package/dist/8930.js +2 -0
- package/dist/8930.js.map +1 -0
- package/dist/942.js +1 -0
- package/dist/942.js.map +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-billing-app.js +1 -1
- package/dist/openmrs-esm-billing-app.js.buildmanifest.json +46 -70
- package/dist/openmrs-esm-billing-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +2 -2
- package/src/bill-item-actions/bill-item-actions.scss +0 -4
- package/src/bill-item-actions/{edit-bill-item.component.tsx → edit-bill-item.modal.tsx} +58 -60
- package/src/bill-item-actions/edit-bill-item.test.tsx +5 -5
- package/src/billable-services/bill-waiver/bill-selection.component.tsx +2 -2
- package/src/billable-services/billable-services-home.component.tsx +1 -1
- package/src/billable-services/billable-services.component.tsx +110 -128
- package/src/billable-services/billable-services.scss +3 -0
- package/src/billable-services/billable-services.test.tsx +1 -3
- package/src/billable-services/cash-point/add-cash-point.modal.tsx +168 -0
- package/src/billable-services/cash-point/cash-point-configuration.component.tsx +16 -191
- package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
- package/src/billable-services/create-edit/add-billable-service.component.tsx +23 -26
- package/src/billable-services/create-edit/add-billable-service.scss +2 -5
- package/src/billable-services/create-edit/edit-billable-service.modal.tsx +50 -0
- package/src/billable-services/payment-modes/add-payment-mode.modal.tsx +121 -0
- package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +72 -0
- package/src/billable-services/payment-modes/payment-modes-config.component.tsx +125 -0
- package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -1
- package/src/billing-form/billing-checkin-form.component.tsx +2 -2
- package/src/billing-form/billing-form.component.tsx +2 -2
- package/src/helpers/functions.ts +5 -4
- package/src/index.ts +16 -6
- package/src/invoice/invoice-table.component.tsx +9 -2
- package/src/invoice/invoice.component.tsx +5 -1
- package/src/invoice/printable-invoice/print-receipt.component.tsx +2 -1
- package/src/modal/require-payment-modal.test.tsx +1 -1
- package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +17 -18
- package/src/routes.json +22 -2
- package/translations/en.json +12 -10
- package/dist/2352.js +0 -1
- package/dist/2352.js.map +0 -1
- package/dist/8638.js +0 -1
- package/dist/8638.js.map +0 -1
- package/dist/929.js +0 -2
- package/dist/929.js.map +0 -1
- package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
- /package/dist/{929.js.LICENSE.txt → 8930.js.LICENSE.txt} +0 -0
|
@@ -2,59 +2,23 @@ import React, { useState, useEffect, useCallback } from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Button,
|
|
4
4
|
DataTable,
|
|
5
|
-
TableContainer,
|
|
6
5
|
Table,
|
|
7
|
-
TableHead,
|
|
8
|
-
TableRow,
|
|
9
|
-
TableHeader,
|
|
10
6
|
TableBody,
|
|
11
7
|
TableCell,
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
Dropdown,
|
|
8
|
+
TableContainer,
|
|
9
|
+
TableHead,
|
|
10
|
+
TableHeader,
|
|
11
|
+
TableRow,
|
|
17
12
|
} from '@carbon/react';
|
|
18
13
|
import { Add } from '@carbon/react/icons';
|
|
19
14
|
import { useTranslation } from 'react-i18next';
|
|
20
|
-
import {
|
|
21
|
-
import { z } from 'zod';
|
|
22
|
-
import { zodResolver } from '@hookform/resolvers/zod';
|
|
23
|
-
import { showSnackbar, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
15
|
+
import { showSnackbar, openmrsFetch, restBaseUrl, showModal, getCoreTranslation } from '@openmrs/esm-framework';
|
|
24
16
|
import { CardHeader } from '@openmrs/esm-patient-common-lib';
|
|
25
17
|
import styles from './cash-point-configuration.scss';
|
|
26
18
|
|
|
27
|
-
// Validation schema
|
|
28
|
-
const cashPointSchema = z.object({
|
|
29
|
-
name: z.string().min(1, 'Cash Point Name is required'),
|
|
30
|
-
uuid: z
|
|
31
|
-
.string()
|
|
32
|
-
.min(1, 'UUID is required')
|
|
33
|
-
.regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i, 'Invalid UUID format'),
|
|
34
|
-
location: z.string().min(1, 'Location is required'),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
type CashPointFormValues = z.infer<typeof cashPointSchema>;
|
|
38
|
-
|
|
39
19
|
const CashPointConfiguration: React.FC = () => {
|
|
40
20
|
const { t } = useTranslation();
|
|
41
21
|
const [cashPoints, setCashPoints] = useState([]);
|
|
42
|
-
const [locations, setLocations] = useState([]);
|
|
43
|
-
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
44
|
-
|
|
45
|
-
const {
|
|
46
|
-
control,
|
|
47
|
-
handleSubmit,
|
|
48
|
-
reset,
|
|
49
|
-
formState: { errors, isSubmitting },
|
|
50
|
-
} = useForm<CashPointFormValues>({
|
|
51
|
-
resolver: zodResolver(cashPointSchema),
|
|
52
|
-
defaultValues: {
|
|
53
|
-
name: '',
|
|
54
|
-
uuid: '',
|
|
55
|
-
location: '',
|
|
56
|
-
},
|
|
57
|
-
});
|
|
58
22
|
|
|
59
23
|
const fetchCashPoints = useCallback(async () => {
|
|
60
24
|
try {
|
|
@@ -62,7 +26,7 @@ const CashPointConfiguration: React.FC = () => {
|
|
|
62
26
|
setCashPoints(response.data.results || []);
|
|
63
27
|
} catch (err) {
|
|
64
28
|
showSnackbar({
|
|
65
|
-
title:
|
|
29
|
+
title: getCoreTranslation('error'),
|
|
66
30
|
subtitle: t('errorFetchingCashPoints', 'An error occurred while fetching cash points.'),
|
|
67
31
|
kind: 'error',
|
|
68
32
|
isLowContrast: false,
|
|
@@ -70,87 +34,14 @@ const CashPointConfiguration: React.FC = () => {
|
|
|
70
34
|
}
|
|
71
35
|
}, [t]);
|
|
72
36
|
|
|
73
|
-
const fetchLocations = useCallback(async () => {
|
|
74
|
-
try {
|
|
75
|
-
const response = await openmrsFetch(`${restBaseUrl}/location?v=default`);
|
|
76
|
-
const allLocations = response.data.results.map((loc) => ({
|
|
77
|
-
id: loc.uuid,
|
|
78
|
-
label: loc.display,
|
|
79
|
-
}));
|
|
80
|
-
setLocations(allLocations);
|
|
81
|
-
} catch (err) {
|
|
82
|
-
showSnackbar({
|
|
83
|
-
title: t('error', 'Error'),
|
|
84
|
-
subtitle: t('errorFetchingLocations', 'An error occurred while fetching locations.'),
|
|
85
|
-
kind: 'error',
|
|
86
|
-
isLowContrast: false,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
}, [t]);
|
|
90
|
-
|
|
91
37
|
useEffect(() => {
|
|
92
38
|
fetchCashPoints();
|
|
93
|
-
|
|
94
|
-
}, [fetchCashPoints, fetchLocations]);
|
|
95
|
-
|
|
96
|
-
const onSubmit = async (data: CashPointFormValues) => {
|
|
97
|
-
const isDuplicate = cashPoints.some(
|
|
98
|
-
(point) => point.name.toLowerCase() === data.name.toLowerCase() || point.uuid === data.uuid,
|
|
99
|
-
);
|
|
39
|
+
}, [fetchCashPoints]);
|
|
100
40
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
'duplicateCashPointError',
|
|
106
|
-
'A cash point with the same name or UUID already exists. Please use a unique name and UUID.',
|
|
107
|
-
),
|
|
108
|
-
kind: 'error',
|
|
109
|
-
isLowContrast: false,
|
|
110
|
-
});
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
try {
|
|
115
|
-
const response = await openmrsFetch(`${restBaseUrl}/billing/cashPoint`, {
|
|
116
|
-
method: 'POST',
|
|
117
|
-
headers: {
|
|
118
|
-
'Content-Type': 'application/json',
|
|
119
|
-
},
|
|
120
|
-
body: {
|
|
121
|
-
name: data.name,
|
|
122
|
-
uuid: data.uuid,
|
|
123
|
-
location: { uuid: data.location },
|
|
124
|
-
},
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
if (response.ok) {
|
|
128
|
-
showSnackbar({
|
|
129
|
-
title: t('success', 'Success'),
|
|
130
|
-
subtitle: t('cashPointSaved', 'Cash point was successfully saved.'),
|
|
131
|
-
kind: 'success',
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
setIsModalOpen(false);
|
|
135
|
-
reset({ name: '', uuid: '', location: '' });
|
|
136
|
-
fetchCashPoints();
|
|
137
|
-
} else {
|
|
138
|
-
const errorData = response.data || {};
|
|
139
|
-
showSnackbar({
|
|
140
|
-
title: t('error', 'Error'),
|
|
141
|
-
subtitle: errorData.message || t('errorSavingCashPoint', 'An error occurred while saving the cash point.'),
|
|
142
|
-
kind: 'error',
|
|
143
|
-
isLowContrast: false,
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
} catch (err) {
|
|
147
|
-
showSnackbar({
|
|
148
|
-
title: t('error', 'Error'),
|
|
149
|
-
subtitle: t('errorSavingCashPoint', 'An error occurred while saving the cash point.'),
|
|
150
|
-
kind: 'error',
|
|
151
|
-
isLowContrast: false,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
41
|
+
const handleAddCashPoint = () => {
|
|
42
|
+
showModal('add-cash-point-modal', {
|
|
43
|
+
onCashPointAdded: fetchCashPoints,
|
|
44
|
+
});
|
|
154
45
|
};
|
|
155
46
|
|
|
156
47
|
const rowData = cashPoints.map((point) => ({
|
|
@@ -164,18 +55,17 @@ const CashPointConfiguration: React.FC = () => {
|
|
|
164
55
|
{ key: 'name', header: t('name', 'Name') },
|
|
165
56
|
{ key: 'uuid', header: t('uuid', 'UUID') },
|
|
166
57
|
{ key: 'location', header: t('location', 'Location') },
|
|
167
|
-
{ key: 'actions', header: t('actions', 'Actions') },
|
|
168
58
|
];
|
|
169
59
|
|
|
170
60
|
return (
|
|
171
61
|
<div className={styles.container}>
|
|
172
62
|
<div className={styles.card}>
|
|
173
63
|
<CardHeader title={t('cashPointHistory', 'Cash Point History')}>
|
|
174
|
-
<Button renderIcon={Add} onClick={
|
|
64
|
+
<Button renderIcon={Add} onClick={handleAddCashPoint} kind="ghost">
|
|
175
65
|
{t('addNewCashPoint', 'Add New Cash Point')}
|
|
176
66
|
</Button>
|
|
177
67
|
</CardHeader>
|
|
178
|
-
<div
|
|
68
|
+
<div>
|
|
179
69
|
<DataTable rows={rowData} headers={headerData} isSortable size="lg">
|
|
180
70
|
{({ rows, headers, getTableProps, getHeaderProps, getRowProps }) => (
|
|
181
71
|
<TableContainer>
|
|
@@ -192,17 +82,9 @@ const CashPointConfiguration: React.FC = () => {
|
|
|
192
82
|
<TableBody>
|
|
193
83
|
{rows.map((row) => (
|
|
194
84
|
<TableRow key={row.id} {...getRowProps({ row })}>
|
|
195
|
-
{row.cells.map((cell) =>
|
|
196
|
-
cell.
|
|
197
|
-
|
|
198
|
-
) : (
|
|
199
|
-
<TableCell key={cell.id}>
|
|
200
|
-
<OverflowMenu>
|
|
201
|
-
<OverflowMenuItem itemText={t('delete', 'Delete')} disabled />
|
|
202
|
-
</OverflowMenu>
|
|
203
|
-
</TableCell>
|
|
204
|
-
),
|
|
205
|
-
)}
|
|
85
|
+
{row.cells.map((cell) => (
|
|
86
|
+
<TableCell key={cell.id}>{cell.value}</TableCell>
|
|
87
|
+
))}
|
|
206
88
|
</TableRow>
|
|
207
89
|
))}
|
|
208
90
|
</TableBody>
|
|
@@ -212,63 +94,6 @@ const CashPointConfiguration: React.FC = () => {
|
|
|
212
94
|
</DataTable>
|
|
213
95
|
</div>
|
|
214
96
|
</div>
|
|
215
|
-
|
|
216
|
-
{/* Modal for Adding New Cash Point */}
|
|
217
|
-
<Modal
|
|
218
|
-
open={isModalOpen}
|
|
219
|
-
modalHeading={t('addCashPoint', 'Add Cash Point')}
|
|
220
|
-
onRequestClose={() => setIsModalOpen(false)}
|
|
221
|
-
onRequestSubmit={handleSubmit(onSubmit)}
|
|
222
|
-
primaryButtonText={t('save', 'Save')}
|
|
223
|
-
secondaryButtonText={t('cancel', 'Cancel')}
|
|
224
|
-
isPrimaryButtonDisabled={isSubmitting}>
|
|
225
|
-
<form>
|
|
226
|
-
<Controller
|
|
227
|
-
name="name"
|
|
228
|
-
control={control}
|
|
229
|
-
render={({ field }) => (
|
|
230
|
-
<TextInput
|
|
231
|
-
id="cash-point-name"
|
|
232
|
-
labelText={t('cashPointName', 'Cash Point Name')}
|
|
233
|
-
placeholder={t('cashPointNamePlaceholder', 'e.g., Pharmacy Cash Point')}
|
|
234
|
-
invalid={!!errors.name}
|
|
235
|
-
invalidText={errors.name?.message}
|
|
236
|
-
{...field}
|
|
237
|
-
/>
|
|
238
|
-
)}
|
|
239
|
-
/>
|
|
240
|
-
<Controller
|
|
241
|
-
name="uuid"
|
|
242
|
-
control={control}
|
|
243
|
-
render={({ field }) => (
|
|
244
|
-
<TextInput
|
|
245
|
-
id="cash-point-uuid"
|
|
246
|
-
labelText={t('cashPointUuid', 'Cash Point UUID')}
|
|
247
|
-
placeholder={t('cashPointUuidPlaceholder', 'Enter UUID')}
|
|
248
|
-
invalid={!!errors.uuid}
|
|
249
|
-
invalidText={errors.uuid?.message}
|
|
250
|
-
{...field}
|
|
251
|
-
/>
|
|
252
|
-
)}
|
|
253
|
-
/>
|
|
254
|
-
<Controller
|
|
255
|
-
name="location"
|
|
256
|
-
control={control}
|
|
257
|
-
render={({ field }) => (
|
|
258
|
-
<Dropdown
|
|
259
|
-
id="cash-point-location"
|
|
260
|
-
label={t('selectLocation', 'Select Location')}
|
|
261
|
-
titleText={t('cashPointLocation', 'Cash Point Location')}
|
|
262
|
-
items={locations}
|
|
263
|
-
selectedItem={locations.find((loc) => loc.id === field.value)}
|
|
264
|
-
onChange={({ selectedItem }) => field.onChange(selectedItem?.id)}
|
|
265
|
-
invalid={!!errors.location}
|
|
266
|
-
invalidText={errors.location?.message}
|
|
267
|
-
/>
|
|
268
|
-
)}
|
|
269
|
-
/>
|
|
270
|
-
</form>
|
|
271
|
-
</Modal>
|
|
272
97
|
</div>
|
|
273
98
|
);
|
|
274
99
|
};
|
|
@@ -16,7 +16,7 @@ import { Controller, useFieldArray, useForm } from 'react-hook-form';
|
|
|
16
16
|
import { useTranslation } from 'react-i18next';
|
|
17
17
|
import { z } from 'zod';
|
|
18
18
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
19
|
-
import { navigate, showSnackbar, useDebounce, useLayoutType } from '@openmrs/esm-framework';
|
|
19
|
+
import { getCoreTranslation, navigate, showSnackbar, useDebounce, useLayoutType } from '@openmrs/esm-framework';
|
|
20
20
|
import {
|
|
21
21
|
createBillableService,
|
|
22
22
|
updateBillableService,
|
|
@@ -32,10 +32,6 @@ type PaymentMode = {
|
|
|
32
32
|
price: string | number;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
type PaymentModeFormValue = {
|
|
36
|
-
payment: Array<PaymentMode>;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
35
|
const servicePriceSchema = z.object({
|
|
40
36
|
paymentMode: z.string().refine((value) => !!value, 'Payment method is required'),
|
|
41
37
|
price: z.union([
|
|
@@ -50,11 +46,12 @@ const paymentFormSchema = z.object({
|
|
|
50
46
|
|
|
51
47
|
const DEFAULT_PAYMENT_OPTION = { paymentMode: '', price: 0 };
|
|
52
48
|
|
|
53
|
-
const AddBillableService: React.FC<{
|
|
54
|
-
editingService
|
|
55
|
-
onClose
|
|
56
|
-
onServiceUpdated
|
|
57
|
-
|
|
49
|
+
const AddBillableService: React.FC<{
|
|
50
|
+
editingService?: any;
|
|
51
|
+
onClose: () => void;
|
|
52
|
+
onServiceUpdated?: () => void;
|
|
53
|
+
isModal?: boolean;
|
|
54
|
+
}> = ({ editingService, onClose, onServiceUpdated, isModal = false }) => {
|
|
58
55
|
const { t } = useTranslation();
|
|
59
56
|
|
|
60
57
|
const { paymentModes, isLoading: isLoadingPaymentModes } = usePaymentModes();
|
|
@@ -179,19 +176,19 @@ const AddBillableService: React.FC<{ editingService?: any; onClose: () => void;
|
|
|
179
176
|
<InlineLoading
|
|
180
177
|
status="active"
|
|
181
178
|
iconDescription={t('loadingDescription', 'Loading')}
|
|
182
|
-
description={t('
|
|
179
|
+
description={t('loadingData', 'Loading data') + '...'}
|
|
183
180
|
/>
|
|
184
181
|
);
|
|
185
182
|
}
|
|
186
183
|
|
|
187
184
|
return (
|
|
188
|
-
<Form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
|
185
|
+
<Form id="billable-service-form" className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
|
189
186
|
<h4>
|
|
190
187
|
{editingService
|
|
191
188
|
? t('editBillableServices', 'Edit Billable Services')
|
|
192
189
|
: t('addBillableServices', 'Add Billable Services')}
|
|
193
190
|
</h4>
|
|
194
|
-
<section
|
|
191
|
+
<section>
|
|
195
192
|
<Layer>
|
|
196
193
|
<TextInput
|
|
197
194
|
id="serviceName"
|
|
@@ -218,7 +215,7 @@ const AddBillableService: React.FC<{ editingService?: any; onClose: () => void;
|
|
|
218
215
|
)}
|
|
219
216
|
</Layer>
|
|
220
217
|
</section>
|
|
221
|
-
<section
|
|
218
|
+
<section>
|
|
222
219
|
<Layer>
|
|
223
220
|
<TextInput
|
|
224
221
|
id="serviceShortName"
|
|
@@ -314,7 +311,7 @@ const AddBillableService: React.FC<{ editingService?: any; onClose: () => void;
|
|
|
314
311
|
);
|
|
315
312
|
})()}
|
|
316
313
|
</section>
|
|
317
|
-
<section
|
|
314
|
+
<section>
|
|
318
315
|
<Layer>
|
|
319
316
|
<ComboBox
|
|
320
317
|
id="serviceType"
|
|
@@ -334,9 +331,8 @@ const AddBillableService: React.FC<{ editingService?: any; onClose: () => void;
|
|
|
334
331
|
/>
|
|
335
332
|
</Layer>
|
|
336
333
|
</section>
|
|
337
|
-
|
|
338
334
|
<section>
|
|
339
|
-
<div
|
|
335
|
+
<div>
|
|
340
336
|
{fields.map((field, index) => (
|
|
341
337
|
<div key={field.id} className={styles.paymentMethodContainer}>
|
|
342
338
|
<Controller
|
|
@@ -388,15 +384,16 @@ const AddBillableService: React.FC<{ editingService?: any; onClose: () => void;
|
|
|
388
384
|
{getPaymentErrorMessage() && <div className={styles.errorMessage}>{getPaymentErrorMessage()}</div>}
|
|
389
385
|
</div>
|
|
390
386
|
</section>
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
387
|
+
{!isModal && (
|
|
388
|
+
<section>
|
|
389
|
+
<Button kind="secondary" onClick={onClose}>
|
|
390
|
+
{getCoreTranslation('cancel')}
|
|
391
|
+
</Button>
|
|
392
|
+
<Button type="submit" disabled={!isValid || Object.keys(errors).length > 0}>
|
|
393
|
+
{getCoreTranslation('save')}
|
|
394
|
+
</Button>
|
|
395
|
+
</section>
|
|
396
|
+
)}
|
|
400
397
|
</Form>
|
|
401
398
|
);
|
|
402
399
|
};
|
|
@@ -8,10 +8,7 @@
|
|
|
8
8
|
flex-direction: column;
|
|
9
9
|
justify-content: space-between;
|
|
10
10
|
height: 100%;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
.section {
|
|
14
|
-
margin: layout.$spacing-03;
|
|
11
|
+
margin: layout.$spacing-05;
|
|
15
12
|
}
|
|
16
13
|
|
|
17
14
|
.sectionTitle {
|
|
@@ -99,7 +96,7 @@
|
|
|
99
96
|
|
|
100
97
|
.conceptLabel {
|
|
101
98
|
@include type.type-style('label-02');
|
|
102
|
-
margin: layout.$spacing-05;
|
|
99
|
+
margin-bottom: layout.$spacing-05;
|
|
103
100
|
}
|
|
104
101
|
|
|
105
102
|
.errorContainer {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
4
|
+
import AddBillableService from './add-billable-service.component';
|
|
5
|
+
import { getCoreTranslation } from '@openmrs/esm-framework';
|
|
6
|
+
|
|
7
|
+
interface EditBillableServiceModalProps {
|
|
8
|
+
closeModal: () => void;
|
|
9
|
+
editingService?: any;
|
|
10
|
+
onServiceUpdated: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const EditBillableServiceModal: React.FC<EditBillableServiceModalProps> = ({
|
|
14
|
+
closeModal,
|
|
15
|
+
editingService,
|
|
16
|
+
onServiceUpdated,
|
|
17
|
+
}) => {
|
|
18
|
+
const { t } = useTranslation();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<>
|
|
22
|
+
<ModalHeader closeModal={closeModal} title={t('billableService', 'Billable Service')} />
|
|
23
|
+
<ModalBody>
|
|
24
|
+
<AddBillableService
|
|
25
|
+
editingService={editingService}
|
|
26
|
+
onClose={closeModal}
|
|
27
|
+
onServiceUpdated={onServiceUpdated}
|
|
28
|
+
isModal={true}
|
|
29
|
+
/>
|
|
30
|
+
</ModalBody>
|
|
31
|
+
<ModalFooter>
|
|
32
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
33
|
+
{getCoreTranslation('cancel')}
|
|
34
|
+
</Button>
|
|
35
|
+
<Button
|
|
36
|
+
onClick={() => {
|
|
37
|
+
// Trigger form submission programmatically
|
|
38
|
+
const form = document.getElementById('billable-service-form') as HTMLFormElement;
|
|
39
|
+
if (form) {
|
|
40
|
+
form.requestSubmit();
|
|
41
|
+
}
|
|
42
|
+
}}>
|
|
43
|
+
{getCoreTranslation('save')}
|
|
44
|
+
</Button>
|
|
45
|
+
</ModalFooter>
|
|
46
|
+
</>
|
|
47
|
+
);
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export default EditBillableServiceModal;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
6
|
+
import { Button, Form, ModalBody, ModalFooter, ModalHeader, Stack, TextInput } from '@carbon/react';
|
|
7
|
+
import { showSnackbar, openmrsFetch, restBaseUrl, getCoreTranslation } from '@openmrs/esm-framework';
|
|
8
|
+
|
|
9
|
+
type PaymentModeFormValues = {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface AddPaymentModeModalProps {
|
|
15
|
+
closeModal: () => void;
|
|
16
|
+
onPaymentModeAdded: () => void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const AddPaymentModeModal: React.FC<AddPaymentModeModalProps> = ({ closeModal, onPaymentModeAdded }) => {
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
|
|
22
|
+
const paymentModeSchema = z.object({
|
|
23
|
+
name: z.string().min(1, t('paymentModeNameRequired', 'Payment Mode Name is required')),
|
|
24
|
+
description: z.string().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const {
|
|
28
|
+
control,
|
|
29
|
+
handleSubmit,
|
|
30
|
+
reset,
|
|
31
|
+
formState: { errors, isSubmitting },
|
|
32
|
+
} = useForm<PaymentModeFormValues>({
|
|
33
|
+
resolver: zodResolver(paymentModeSchema),
|
|
34
|
+
defaultValues: {
|
|
35
|
+
name: '',
|
|
36
|
+
description: '',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const onSubmit = async (data: PaymentModeFormValues) => {
|
|
41
|
+
try {
|
|
42
|
+
await openmrsFetch(`${restBaseUrl}/billing/paymentMode`, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
},
|
|
47
|
+
body: {
|
|
48
|
+
name: data.name,
|
|
49
|
+
description: data.description || '',
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
showSnackbar({
|
|
54
|
+
title: t('success', 'Success'),
|
|
55
|
+
subtitle: t('paymentModeSaved', 'Payment mode was successfully saved.'),
|
|
56
|
+
kind: 'success',
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
closeModal();
|
|
60
|
+
reset({ name: '', description: '' });
|
|
61
|
+
onPaymentModeAdded();
|
|
62
|
+
} catch (err) {
|
|
63
|
+
showSnackbar({
|
|
64
|
+
title: getCoreTranslation('error'),
|
|
65
|
+
subtitle: err?.message || t('errorSavingPaymentMode', 'An error occurred while saving the payment mode.'),
|
|
66
|
+
kind: 'error',
|
|
67
|
+
isLowContrast: false,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<>
|
|
74
|
+
<ModalHeader closeModal={closeModal} title={t('addPaymentMode', 'Add Payment Mode')} />
|
|
75
|
+
<Form onSubmit={handleSubmit(onSubmit)}>
|
|
76
|
+
<ModalBody>
|
|
77
|
+
<Stack gap={5}>
|
|
78
|
+
<Controller
|
|
79
|
+
name="name"
|
|
80
|
+
control={control}
|
|
81
|
+
render={({ field }) => (
|
|
82
|
+
<TextInput
|
|
83
|
+
id="payment-mode-name"
|
|
84
|
+
labelText={t('paymentModeName', 'Payment Mode Name')}
|
|
85
|
+
placeholder={t('paymentModeNamePlaceholder', 'e.g., Cash, Credit Card')}
|
|
86
|
+
invalid={!!errors.name}
|
|
87
|
+
invalidText={errors.name?.message}
|
|
88
|
+
{...field}
|
|
89
|
+
/>
|
|
90
|
+
)}
|
|
91
|
+
/>
|
|
92
|
+
<Controller
|
|
93
|
+
name="description"
|
|
94
|
+
control={control}
|
|
95
|
+
render={({ field }) => (
|
|
96
|
+
<TextInput
|
|
97
|
+
id="payment-mode-description"
|
|
98
|
+
labelText={t('description', 'Description')}
|
|
99
|
+
placeholder={t('descriptionPlaceholder', 'e.g., Used for all cash transactions')}
|
|
100
|
+
invalid={!!errors.description}
|
|
101
|
+
invalidText={errors.description?.message}
|
|
102
|
+
{...field}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
105
|
+
/>
|
|
106
|
+
</Stack>
|
|
107
|
+
</ModalBody>
|
|
108
|
+
<ModalFooter>
|
|
109
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
110
|
+
{getCoreTranslation('cancel')}
|
|
111
|
+
</Button>
|
|
112
|
+
<Button type="submit" disabled={isSubmitting}>
|
|
113
|
+
{isSubmitting ? t('saving', 'Saving') + '...' : getCoreTranslation('save')}
|
|
114
|
+
</Button>
|
|
115
|
+
</ModalFooter>
|
|
116
|
+
</Form>
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default AddPaymentModeModal;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
4
|
+
import { showSnackbar, openmrsFetch, restBaseUrl, getCoreTranslation } from '@openmrs/esm-framework';
|
|
5
|
+
|
|
6
|
+
interface DeletePaymentModeModalProps {
|
|
7
|
+
closeModal: () => void;
|
|
8
|
+
paymentModeUuid: string;
|
|
9
|
+
paymentModeName: string;
|
|
10
|
+
onPaymentModeDeleted: () => void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const DeletePaymentModeModal: React.FC<DeletePaymentModeModalProps> = ({
|
|
14
|
+
closeModal,
|
|
15
|
+
paymentModeUuid,
|
|
16
|
+
paymentModeName,
|
|
17
|
+
onPaymentModeDeleted,
|
|
18
|
+
}) => {
|
|
19
|
+
const { t } = useTranslation();
|
|
20
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
21
|
+
|
|
22
|
+
const handleDelete = async () => {
|
|
23
|
+
setIsDeleting(true);
|
|
24
|
+
try {
|
|
25
|
+
await openmrsFetch(`${restBaseUrl}/billing/paymentMode/${paymentModeUuid}`, {
|
|
26
|
+
method: 'DELETE',
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
showSnackbar({
|
|
30
|
+
title: t('success', 'Success'),
|
|
31
|
+
subtitle: t('paymentModeDeleted', 'Payment mode was successfully deleted.'),
|
|
32
|
+
kind: 'success',
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
closeModal();
|
|
36
|
+
onPaymentModeDeleted();
|
|
37
|
+
} catch (err) {
|
|
38
|
+
showSnackbar({
|
|
39
|
+
title: getCoreTranslation('error'),
|
|
40
|
+
subtitle: err?.message || t('errorDeletingPaymentMode', 'An error occurred while deleting the payment mode.'),
|
|
41
|
+
kind: 'error',
|
|
42
|
+
isLowContrast: false,
|
|
43
|
+
});
|
|
44
|
+
} finally {
|
|
45
|
+
setIsDeleting(false);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<>
|
|
51
|
+
<ModalHeader closeModal={closeModal} title={t('deletePaymentMode', 'Delete Payment Mode')} />
|
|
52
|
+
<ModalBody>
|
|
53
|
+
<p>{t('confirmDeleteMessage', 'Are you sure you want to delete this payment mode? Proceed cautiously.')}</p>
|
|
54
|
+
{paymentModeName && (
|
|
55
|
+
<p>
|
|
56
|
+
<strong>{t('paymentModeName', 'Payment Mode Name: {{paymentModeName}}', { paymentModeName })}</strong>
|
|
57
|
+
</p>
|
|
58
|
+
)}
|
|
59
|
+
</ModalBody>
|
|
60
|
+
<ModalFooter>
|
|
61
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
62
|
+
{getCoreTranslation('cancel')}
|
|
63
|
+
</Button>
|
|
64
|
+
<Button kind="danger" onClick={handleDelete} disabled={isDeleting}>
|
|
65
|
+
{isDeleting ? t('deleting', 'Deleting') + '...' : getCoreTranslation('delete')}
|
|
66
|
+
</Button>
|
|
67
|
+
</ModalFooter>
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export default DeletePaymentModeModal;
|