@openmrs/esm-billing-app 1.0.2-pre.715 → 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.
Files changed (55) hide show
  1. package/dist/4300.js +1 -1
  2. package/dist/4724.js +1 -1
  3. package/dist/4724.js.map +1 -1
  4. package/dist/4739.js +1 -1
  5. package/dist/4739.js.map +1 -1
  6. package/dist/7452.js +1 -1
  7. package/dist/7452.js.map +1 -1
  8. package/dist/8930.js +2 -0
  9. package/dist/8930.js.map +1 -0
  10. package/dist/942.js +1 -0
  11. package/dist/942.js.map +1 -0
  12. package/dist/main.js +1 -1
  13. package/dist/main.js.map +1 -1
  14. package/dist/openmrs-esm-billing-app.js +1 -1
  15. package/dist/openmrs-esm-billing-app.js.buildmanifest.json +46 -70
  16. package/dist/openmrs-esm-billing-app.js.map +1 -1
  17. package/dist/routes.json +1 -1
  18. package/package.json +2 -2
  19. package/src/bill-item-actions/bill-item-actions.scss +0 -4
  20. package/src/bill-item-actions/{edit-bill-item.component.tsx → edit-bill-item.modal.tsx} +58 -60
  21. package/src/bill-item-actions/edit-bill-item.test.tsx +5 -5
  22. package/src/billable-services/bill-waiver/bill-selection.component.tsx +2 -2
  23. package/src/billable-services/billable-services-home.component.tsx +1 -1
  24. package/src/billable-services/billable-services.component.tsx +110 -128
  25. package/src/billable-services/billable-services.scss +3 -0
  26. package/src/billable-services/billable-services.test.tsx +1 -3
  27. package/src/billable-services/cash-point/add-cash-point.modal.tsx +168 -0
  28. package/src/billable-services/cash-point/cash-point-configuration.component.tsx +16 -191
  29. package/src/billable-services/cash-point/cash-point-configuration.scss +1 -5
  30. package/src/billable-services/create-edit/add-billable-service.component.tsx +23 -26
  31. package/src/billable-services/create-edit/add-billable-service.scss +2 -5
  32. package/src/billable-services/create-edit/edit-billable-service.modal.tsx +50 -0
  33. package/src/billable-services/payment-modes/add-payment-mode.modal.tsx +121 -0
  34. package/src/billable-services/payment-modes/delete-payment-mode.modal.tsx +72 -0
  35. package/src/billable-services/payment-modes/payment-modes-config.component.tsx +125 -0
  36. package/src/billable-services/{payyment-modes → payment-modes}/payment-modes-config.scss +5 -1
  37. package/src/billing-form/billing-checkin-form.component.tsx +2 -2
  38. package/src/billing-form/billing-form.component.tsx +2 -2
  39. package/src/helpers/functions.ts +5 -4
  40. package/src/index.ts +16 -6
  41. package/src/invoice/invoice-table.component.tsx +9 -2
  42. package/src/invoice/invoice.component.tsx +5 -1
  43. package/src/invoice/printable-invoice/print-receipt.component.tsx +2 -1
  44. package/src/modal/require-payment-modal.test.tsx +1 -1
  45. package/src/modal/{require-payment-modal.component.tsx → require-payment.modal.tsx} +17 -18
  46. package/src/routes.json +22 -2
  47. package/translations/en.json +12 -10
  48. package/dist/2352.js +0 -1
  49. package/dist/2352.js.map +0 -1
  50. package/dist/8638.js +0 -1
  51. package/dist/8638.js.map +0 -1
  52. package/dist/929.js +0 -2
  53. package/dist/929.js.map +0 -1
  54. package/src/billable-services/payyment-modes/payment-modes-config.component.tsx +0 -280
  55. /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
- Modal,
13
- TextInput,
14
- OverflowMenu,
15
- OverflowMenuItem,
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 { useForm, Controller } from 'react-hook-form';
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: t('error', 'Error'),
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
- fetchLocations();
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
- if (isDuplicate) {
102
- showSnackbar({
103
- title: t('error', 'Error'),
104
- subtitle: t(
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={() => setIsModalOpen(true)} kind="ghost">
64
+ <Button renderIcon={Add} onClick={handleAddCashPoint} kind="ghost">
175
65
  {t('addNewCashPoint', 'Add New Cash Point')}
176
66
  </Button>
177
67
  </CardHeader>
178
- <div className={styles.billHistoryContainer}>
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.info.header !== 'actions' ? (
197
- <TableCell key={cell.id}>{cell.value}</TableCell>
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
  };
@@ -13,11 +13,7 @@
13
13
  padding: layout.$spacing-05;
14
14
  }
15
15
 
16
- .billHistoryContainer {
17
- margin-top: layout.$spacing-05;
18
- }
19
-
20
16
  .table {
21
17
  width: 100%;
22
18
  table-layout: auto;
23
- }
19
+ }
@@ -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<{ editingService?: any; onClose: () => void; onServiceUpdated?: () => void }> = ({
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('loading', 'Loading data...')}
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 className={styles.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 className={styles.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 className={styles.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 className={styles.container}>
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
- <section>
393
- <Button kind="secondary" onClick={onClose}>
394
- {t('cancel', 'Cancel')}
395
- </Button>
396
- <Button type="submit" disabled={!isValid || Object.keys(errors).length > 0}>
397
- {t('save', 'Save')}
398
- </Button>
399
- </section>
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;