@digihmis/esm-administration-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 (206) hide show
  1. package/README.md +4 -0
  2. package/dist/101.js +1 -0
  3. package/dist/101.js.map +1 -0
  4. package/dist/117.js +1 -0
  5. package/dist/117.js.map +1 -0
  6. package/dist/132.js +1 -0
  7. package/dist/132.js.map +1 -0
  8. package/dist/15.js +1 -0
  9. package/dist/15.js.map +1 -0
  10. package/dist/152.js +1 -0
  11. package/dist/152.js.map +1 -0
  12. package/dist/153.js +1 -0
  13. package/dist/153.js.map +1 -0
  14. package/dist/190.js +1 -0
  15. package/dist/190.js.map +1 -0
  16. package/dist/209.js +1 -0
  17. package/dist/209.js.map +1 -0
  18. package/dist/266.js +1 -0
  19. package/dist/266.js.map +1 -0
  20. package/dist/317.js +1 -0
  21. package/dist/317.js.map +1 -0
  22. package/dist/349.js +1 -0
  23. package/dist/349.js.map +1 -0
  24. package/dist/371.js +1 -0
  25. package/dist/371.js.map +1 -0
  26. package/dist/378.js +1 -0
  27. package/dist/378.js.map +1 -0
  28. package/dist/394.js +1 -0
  29. package/dist/394.js.map +1 -0
  30. package/dist/442.js +1 -0
  31. package/dist/442.js.map +1 -0
  32. package/dist/45.js +1 -0
  33. package/dist/45.js.map +1 -0
  34. package/dist/454.js +1 -0
  35. package/dist/454.js.map +1 -0
  36. package/dist/466.js +1 -0
  37. package/dist/466.js.map +1 -0
  38. package/dist/482.js +1 -0
  39. package/dist/482.js.map +1 -0
  40. package/dist/501.js +1 -0
  41. package/dist/501.js.map +1 -0
  42. package/dist/508.js +1 -0
  43. package/dist/508.js.map +1 -0
  44. package/dist/578.js +1 -0
  45. package/dist/578.js.map +1 -0
  46. package/dist/598.js +1 -0
  47. package/dist/598.js.map +1 -0
  48. package/dist/606.js +1 -0
  49. package/dist/606.js.map +1 -0
  50. package/dist/61.js +1 -0
  51. package/dist/61.js.map +1 -0
  52. package/dist/640.js +1 -0
  53. package/dist/640.js.map +1 -0
  54. package/dist/685.js +1 -0
  55. package/dist/685.js.map +1 -0
  56. package/dist/689.js +1 -0
  57. package/dist/689.js.map +1 -0
  58. package/dist/709.js +1 -0
  59. package/dist/709.js.map +1 -0
  60. package/dist/712.js +1 -0
  61. package/dist/712.js.map +1 -0
  62. package/dist/720.js +1 -0
  63. package/dist/720.js.map +1 -0
  64. package/dist/737.js +12 -0
  65. package/dist/737.js.map +1 -0
  66. package/dist/742.js +1 -0
  67. package/dist/742.js.map +1 -0
  68. package/dist/747.js +1 -0
  69. package/dist/747.js.map +1 -0
  70. package/dist/771.js +1 -0
  71. package/dist/771.js.map +1 -0
  72. package/dist/806.js +1 -0
  73. package/dist/806.js.map +1 -0
  74. package/dist/912.js +1 -0
  75. package/dist/912.js.map +1 -0
  76. package/dist/913.js +1 -0
  77. package/dist/913.js.map +1 -0
  78. package/dist/916.js +15 -0
  79. package/dist/916.js.map +1 -0
  80. package/dist/940.js +1 -0
  81. package/dist/940.js.map +1 -0
  82. package/dist/digihmis-esm-administration-app.js +6 -0
  83. package/dist/digihmis-esm-administration-app.js.buildmanifest.json +1100 -0
  84. package/dist/digihmis-esm-administration-app.js.map +1 -0
  85. package/dist/main.js +17 -0
  86. package/dist/main.js.map +1 -0
  87. package/dist/routes.json +1 -0
  88. package/increment-version.js +17 -0
  89. package/jest.config.js +26 -0
  90. package/package.json +51 -0
  91. package/release-version.js +16 -0
  92. package/rspack.config.js +1 -0
  93. package/src/bed-management/bed-management.component.tsx +19 -0
  94. package/src/bed-management/bed-management.scss +0 -0
  95. package/src/billing-management/billing-charge-items/billing-charge-items-modal/billing-charge-items.modal.tsx +79 -0
  96. package/src/billing-management/billing-charge-items/billing-charge-items-modal/billing-charge-items.resource.ts +11 -0
  97. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/billing-charge-item.workspace.tsx +212 -0
  98. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/billing-charge-items.resource.ts +51 -0
  99. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/billing-charge-service.workspace.tsx +296 -0
  100. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/components/concept-search.component.tsx +67 -0
  101. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/components/price.component.tsx +81 -0
  102. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/components/stock-search.component.tsx +107 -0
  103. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/global/billing-charge-commodity.scss +61 -0
  104. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/global/billing-charge-items.scss +59 -0
  105. package/src/billing-management/billing-charge-items/billing-charge-items-workspace/schemas/form-schemas.ts +33 -0
  106. package/src/billing-management/billing-charge-items/billing-charge-items.component.tsx +222 -0
  107. package/src/billing-management/billing-charge-items/billing-charge-items.resource.ts +20 -0
  108. package/src/billing-management/billing-charge-items/billing-charge-items.scss +84 -0
  109. package/src/billing-management/billing-charge-items/type/index.ts +88 -0
  110. package/src/billing-management/billing-charge-items/utils/index.ts +96 -0
  111. package/src/billing-management/billing-management.component.tsx +21 -0
  112. package/src/billing-management/billing-management.scss +0 -0
  113. package/src/billing-management/billing-payment-modes/billing-payment-modes-modal/billing-delete-payment-modes.modal.scss +28 -0
  114. package/src/billing-management/billing-payment-modes/billing-payment-modes-modal/billing-delete-payment-modes.modal.tsx +67 -0
  115. package/src/billing-management/billing-payment-modes/billing-payment-modes-modal/delete-payment-mode.test.tsx +56 -0
  116. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/billing-payment-modes-form-schema.ts +52 -0
  117. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/billing-payment-modes.resource.ts +35 -0
  118. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/billing-payment-modes.workspace.scss +33 -0
  119. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/billing-payment-modes.workspace.test.tsx +239 -0
  120. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/billing-payment-modes.workspace.tsx +218 -0
  121. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/payment-attributes/payment-mode-attributes.component.tsx +156 -0
  122. package/src/billing-management/billing-payment-modes/billing-payment-modes-workspace/payment-attributes/payment-mode-attributes.scss +10 -0
  123. package/src/billing-management/billing-payment-modes/billing-payment-modes.component.tsx +243 -0
  124. package/src/billing-management/billing-payment-modes/billing-payment-modes.resource.ts +23 -0
  125. package/src/billing-management/billing-payment-modes/billing-payment-modes.scss +140 -0
  126. package/src/billing-management/billing-payment-modes/type/index.ts +42 -0
  127. package/src/billing-management/billing-payment-points/billing-payment-point-modal/billing-delete-payment-modes.modal.scss +28 -0
  128. package/src/billing-management/billing-payment-points/billing-payment-point-modal/billing-delete-payment-point.modal.tsx +72 -0
  129. package/src/billing-management/billing-payment-points/billing-payment-point-modal/delete-payment-point.test.tsx +56 -0
  130. package/src/billing-management/billing-payment-points/billing-payment-point.component.tsx +202 -0
  131. package/src/billing-management/billing-payment-points/billing-payment-point.resource.ts +16 -0
  132. package/src/billing-management/billing-payment-points/billing-payment-point.scss +0 -0
  133. package/src/billing-management/billing-payment-points/billing-payment-points-workspace/billing-payment-point.resource.ts +39 -0
  134. package/src/billing-management/billing-payment-points/billing-payment-points-workspace/billing-payment-point.workspace.scss +33 -0
  135. package/src/billing-management/billing-payment-points/billing-payment-points-workspace/billing-payment-points.workspace.tsx +194 -0
  136. package/src/billing-management/billing-payment-points/type/index.ts +35 -0
  137. package/src/billing-management/billing-tabs/billing-tabs.component.scss +15 -0
  138. package/src/billing-management/billing-tabs/billing-tabs.component.tsx +36 -0
  139. package/src/components/custom-icon-wrapper/custom-icon-wrapper.component.tsx +17 -0
  140. package/src/components/custom-page-header/custom-page-header.component.tsx +38 -0
  141. package/src/components/custom-page-header/custom-page-header.scss +76 -0
  142. package/src/config-schema.ts +142 -0
  143. package/src/constant/index.ts +1 -0
  144. package/src/dashboard.meta.ts +47 -0
  145. package/src/declarations.d.ts +6 -0
  146. package/src/form-builder/form-builder.component.tsx +19 -0
  147. package/src/form-builder/form-builder.scss +0 -0
  148. package/src/hooks/useConcept.ts +13 -0
  149. package/src/hooks/useQueues.ts +22 -0
  150. package/src/hooks/useSystemSetting.ts +18 -0
  151. package/src/index.ts +133 -0
  152. package/src/left-panel/left-panel.component.tsx +49 -0
  153. package/src/left-panel/left-panel.scss +13 -0
  154. package/src/location-management/components/auto-suggest/autosuggest.component.tsx +149 -0
  155. package/src/location-management/components/auto-suggest/autosuggest.scss +61 -0
  156. package/src/location-management/components/auto-suggest/location-autosuggest.component.tsx +97 -0
  157. package/src/location-management/components/auto-suggest/location-autosuggest.scss +48 -0
  158. package/src/location-management/components/custom-results-tile/results-tile.component.tsx +43 -0
  159. package/src/location-management/components/custom-results-tile/results-tile.scss +86 -0
  160. package/src/location-management/forms/add-location/add-location.workspace.scss +71 -0
  161. package/src/location-management/forms/add-location/add-location.workspace.tsx +199 -0
  162. package/src/location-management/forms/search-location/search-location.workspace.scss +71 -0
  163. package/src/location-management/forms/search-location/search-location.workspace.tsx +214 -0
  164. package/src/location-management/helpers/index.ts +33 -0
  165. package/src/location-management/hooks/UseFacilityLocations.ts +12 -0
  166. package/src/location-management/hooks/useLocation.ts +18 -0
  167. package/src/location-management/hooks/useLocationTags.ts +15 -0
  168. package/src/location-management/location-management-dashboard.component.tsx +21 -0
  169. package/src/location-management/location-management-dashboard.scss +3 -0
  170. package/src/location-management/tables/locations-table.component.tsx +242 -0
  171. package/src/location-management/tables/locations-table.resource.ts +26 -0
  172. package/src/location-management/tables/locations-table.scss +114 -0
  173. package/src/location-management/types/index.ts +120 -0
  174. package/src/queue-management/queue-management-dashboard.component.tsx +23 -0
  175. package/src/queue-management/queue-management-dashboard.scss +8 -0
  176. package/src/queue-management/queue-management-table/queue-management-table.component.tsx +296 -0
  177. package/src/queue-management/queue-management-table/queue-management-table.scss +78 -0
  178. package/src/queue-management/queue-management-table/queue-management.resource.ts +39 -0
  179. package/src/queue-management/queue-management-workspace/queue-room/queue-room-form.scss +56 -0
  180. package/src/queue-management/queue-management-workspace/queue-room/queue-room-form.test.tsx +82 -0
  181. package/src/queue-management/queue-management-workspace/queue-room/queue-room-form.workspace.tsx +169 -0
  182. package/src/queue-management/queue-management-workspace/queue-room/queue-room.resource.ts +20 -0
  183. package/src/queue-management/queue-management-workspace/queue-service/queue-service-form.scss +61 -0
  184. package/src/queue-management/queue-management-workspace/queue-service/queue-service-form.test.tsx +127 -0
  185. package/src/queue-management/queue-management-workspace/queue-service/queue-service-form.workspace.tsx +178 -0
  186. package/src/queue-management/queue-management-workspace/queue-service/queue-service.resource.ts +55 -0
  187. package/src/queue-management/queue-types/index.ts +33 -0
  188. package/src/routes.json +157 -0
  189. package/src/setup-tests.ts +1 -0
  190. package/src/system-info/system-info.component.tsx +338 -0
  191. package/src/system-info/system-info.resource.ts +40 -0
  192. package/src/system-info/system-info.scss +622 -0
  193. package/src/type/index.ts +6 -0
  194. package/translations/am.json +111 -0
  195. package/translations/ar.json +115 -0
  196. package/translations/en.json +111 -0
  197. package/translations/es.json +112 -0
  198. package/translations/fr.json +112 -0
  199. package/translations/he.json +112 -0
  200. package/translations/km.json +110 -0
  201. package/translations/pt.json +112 -0
  202. package/translations/sw.json +111 -0
  203. package/translations/vi.json +110 -0
  204. package/translations/zh.json +110 -0
  205. package/tsconfig.json +5 -0
  206. package/version.js +27 -0
@@ -0,0 +1,72 @@
1
+ import React from 'react';
2
+ import { ModalBody, ModalFooter, Button, ModalHeader } from '@carbon/react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
5
+ import styles from './billing-delete-payment-modes.modal.scss';
6
+ import { PaymentPoint } from '../type';
7
+ import { deletePaymentPoint, handleMutation } from '../billing-payment-points-workspace/billing-payment-point.resource';
8
+
9
+ type DeletePaymentPointModalProps = {
10
+ closeModal: () => void;
11
+ paymentPoint: PaymentPoint;
12
+ };
13
+
14
+ const DeletePaymentPointModal: React.FC<DeletePaymentPointModalProps> = ({ closeModal, paymentPoint }) => {
15
+ const { t } = useTranslation();
16
+ const paymentPointName = paymentPoint?.name || '';
17
+
18
+ const handleDelete = async () => {
19
+ try {
20
+ await deletePaymentPoint(paymentPoint.uuid);
21
+ showSnackbar({
22
+ title: t('paymentPointDeleted', 'Payment point {{paymentPointName}} successfully deleted', {
23
+ paymentPointName,
24
+ }),
25
+ subtitle: t('paymentPointDeletedSubtitle', 'The payment point has been deleted'),
26
+ kind: 'success',
27
+ isLowContrast: true,
28
+ timeoutInMs: 5000,
29
+ autoClose: true,
30
+ });
31
+ handleMutation(`${restBaseUrl}/cashier/cashPoint?v=full`);
32
+ closeModal();
33
+ } catch (error) {
34
+ showSnackbar({
35
+ title: t('paymentPointDeletedError', 'Error deleting payment point'),
36
+ subtitle: t(
37
+ 'paymentPointDeletedErrorSubtitle',
38
+ 'An error occurred while deleting the payment point. Please try again.',
39
+ ),
40
+ kind: 'error',
41
+ isLowContrast: true,
42
+ timeoutInMs: 5000,
43
+ autoClose: true,
44
+ });
45
+ }
46
+ };
47
+
48
+ return (
49
+ <>
50
+ <ModalHeader className={styles.modalHeaderLabel} closeModal={closeModal}>
51
+ {t('deletePaymentMode', 'Delete Payment Mode')}
52
+ </ModalHeader>
53
+ <ModalBody className={styles.modalHeaderHeading}>
54
+ {t(
55
+ 'deletePaymentPointDescription',
56
+ `Are you sure you want to delete this {{paymentPointName}} payment point? Proceed cautiously.`,
57
+ { paymentPointName },
58
+ )}
59
+ </ModalBody>
60
+ <ModalFooter>
61
+ <Button kind="secondary" onClick={closeModal}>
62
+ {t('cancel', 'Cancel')}
63
+ </Button>
64
+ <Button kind="danger" onClick={handleDelete}>
65
+ {t('delete', 'Delete')}
66
+ </Button>
67
+ </ModalFooter>
68
+ </>
69
+ );
70
+ };
71
+
72
+ export default DeletePaymentPointModal;
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import DeletePaymentModeModal from './billing-delete-payment-point.modal';
5
+ import { PaymentMethod } from '../type';
6
+ import { deletePaymentMode } from '../billing-payment-modes-workspace/billing-payment-modes.resource';
7
+
8
+ const mockDeletePaymentMode = jest.mocked(deletePaymentMode);
9
+
10
+ jest.mock('./payment-mode.resource', () => ({
11
+ deletePaymentMode: jest.fn(),
12
+ }));
13
+
14
+ describe('DeletePaymentModeModal', () => {
15
+ test('should be able to delete payment mode when user clicks on delete button', async () => {
16
+ const user = userEvent.setup();
17
+ const mockCloseModal = jest.fn();
18
+ const mockPaymentMode = {
19
+ uuid: '123',
20
+ name: 'Test Payment Mode',
21
+ description: 'Test Description',
22
+ retired: false,
23
+ auditInfo: {
24
+ creator: {
25
+ uuid: '456',
26
+ display: 'Test Creator',
27
+ },
28
+ dateCreated: '2021-01-01',
29
+ },
30
+ } as PaymentMethod;
31
+
32
+ render(<DeletePaymentModeModal closeModal={mockCloseModal} paymentMode={mockPaymentMode} />);
33
+
34
+ const deleteButton = await screen.findByRole('button', { name: /Delete/i });
35
+ await user.click(deleteButton);
36
+
37
+ expect(mockDeletePaymentMode).toHaveBeenCalledWith(mockPaymentMode.uuid);
38
+ });
39
+
40
+ test('should be able to cancel deletion of payment mode when user clicks on cancel button', async () => {
41
+ const user = userEvent.setup();
42
+ const mockCloseModal = jest.fn();
43
+ const mockPaymentMode = {
44
+ uuid: '123',
45
+ name: 'Test Payment Mode',
46
+ description: 'Test Description',
47
+ } as PaymentMethod;
48
+
49
+ render(<DeletePaymentModeModal closeModal={mockCloseModal} paymentMode={mockPaymentMode} />);
50
+
51
+ const cancelButton = await screen.findByRole('button', { name: /Cancel/i });
52
+ await user.click(cancelButton);
53
+
54
+ expect(mockCloseModal).toHaveBeenCalled();
55
+ });
56
+ });
@@ -0,0 +1,202 @@
1
+ import React, { useMemo, useState } from 'react';
2
+ import {
3
+ Button,
4
+ DataTable,
5
+ DataTableSkeleton,
6
+ Table,
7
+ TableBody,
8
+ TableCell,
9
+ TableContainer,
10
+ TableHead,
11
+ TableHeader,
12
+ TableRow,
13
+ Layer,
14
+ Pagination,
15
+ Tile,
16
+ IconButton,
17
+ } from '@carbon/react';
18
+ import { Add, Edit, TrashCan } from '@carbon/react/icons';
19
+ import { launchWorkspace, useLayoutType, ErrorState, showModal } from '@openmrs/esm-framework';
20
+ import { useTranslation } from 'react-i18next';
21
+ import styles from './billing-payment-point.scss';
22
+ import { CardHeader } from '@openmrs/esm-patient-common-lib';
23
+ import { usePaymentPoints } from './billing-payment-point.resource';
24
+ import { PaymentPoint } from './type';
25
+
26
+ const PaymentPointTable = () => {
27
+ const { t } = useTranslation();
28
+ const layout = useLayoutType();
29
+ const isTablet = layout === 'tablet';
30
+ const responsiveSize = isTablet ? 'lg' : 'sm';
31
+
32
+ const { paymentPoints, isLoading, error } = usePaymentPoints();
33
+
34
+ const [currentPage, setCurrentPage] = useState(1);
35
+ const [currentPageSize, setCurrentPageSize] = useState(10);
36
+ const pageSizes = [10, 20, 30, 40, 50];
37
+
38
+ const headers = [
39
+ {
40
+ key: 'dateCreated',
41
+ header: t('dateCreated', 'Date Created'),
42
+ },
43
+ {
44
+ key: 'name',
45
+ header: t('name', 'Name'),
46
+ },
47
+ {
48
+ key: 'description',
49
+ header: t('description', 'Description'),
50
+ },
51
+ {
52
+ key: 'retired',
53
+ header: t('retired', 'Retired'),
54
+ },
55
+ ];
56
+
57
+ const tableRows = useMemo(() => {
58
+ if (!paymentPoints || !Array.isArray(paymentPoints)) {
59
+ return [];
60
+ }
61
+
62
+ return paymentPoints.map((point) => ({
63
+ id: point.uuid,
64
+ dateCreated: point?.auditInfo?.dateCreated ? new Date(point.auditInfo.dateCreated).toLocaleDateString() : '-',
65
+ name: point.name || '-',
66
+ description: point.description || '-',
67
+ retired: point.retired ? t('yes', 'Yes') : t('no', 'No'),
68
+ paymentPoint: point,
69
+ }));
70
+ }, [paymentPoints, t]);
71
+
72
+ const paginatedRows = useMemo(() => {
73
+ const startIndex = (currentPage - 1) * currentPageSize;
74
+ const endIndex = startIndex + currentPageSize;
75
+ return tableRows.slice(startIndex, endIndex);
76
+ }, [tableRows, currentPage, currentPageSize]);
77
+
78
+ const handleAddPaymentPoint = () => {
79
+ launchWorkspace('payment-point-workspace', {
80
+ workspaceTitle: t('addPaymentPoint', 'Add Payment Point'),
81
+ });
82
+ };
83
+
84
+ const showDeletePaymentPointModal = (paymentPoint: PaymentPoint) => {
85
+ const dispose = showModal('delete-payment-point-modal', {
86
+ closeModal: () => dispose(),
87
+ paymentPoint,
88
+ });
89
+ };
90
+
91
+ if (isLoading) {
92
+ return (
93
+ <div className={styles.section}>
94
+ <h3>{t('paymentPoints', 'Payment Points')}</h3>
95
+ <DataTableSkeleton role="progressbar" compact={!isTablet} zebra />
96
+ </div>
97
+ );
98
+ }
99
+
100
+ if (error) {
101
+ return <ErrorState error={error} headerTitle={t('paymentPoints', 'Payment Points')} />;
102
+ }
103
+
104
+ return (
105
+ <div className={styles.section}>
106
+ <CardHeader title={t('paymentPoints', 'Payment Points')}>
107
+ <Button kind="ghost" renderIcon={(props) => <Add size={16} {...props} />} onClick={handleAddPaymentPoint}>
108
+ {t('addPaymentPoint', 'Add Payment Point')}
109
+ </Button>
110
+ </CardHeader>
111
+ <Layer>
112
+ <DataTable rows={paginatedRows} headers={headers} isSortable size={responsiveSize} useZebraStyles>
113
+ {({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => (
114
+ <TableContainer>
115
+ <Table {...getTableProps()} aria-label={t('paymentPointsTable', 'Payment points table')}>
116
+ <TableHead>
117
+ <TableRow>
118
+ {headers.map((header) => (
119
+ <TableHeader key={header.key} {...getHeaderProps({ header })}>
120
+ {header.header}
121
+ </TableHeader>
122
+ ))}
123
+ <TableHeader>{t('actions', 'Actions')}</TableHeader>
124
+ </TableRow>
125
+ </TableHead>
126
+ <TableBody>
127
+ {rows.map((row, index) => {
128
+ const paymentPoint = paginatedRows[index]?.paymentPoint;
129
+ return (
130
+ <TableRow key={row.id} {...getRowProps({ row })}>
131
+ {row.cells.map((cell) => (
132
+ <TableCell key={cell.id}>{cell.value}</TableCell>
133
+ ))}
134
+ <TableCell>
135
+ <div style={{ display: 'flex', gap: '0.5rem' }}>
136
+ <IconButton
137
+ kind="ghost"
138
+ size={responsiveSize}
139
+ label={t('edit', 'Edit')}
140
+ onClick={() =>
141
+ launchWorkspace('payment-point-workspace', {
142
+ workspaceTitle: t('editPaymentPoint', 'Edit Payment Point'),
143
+ initialPaymentPoint: paymentPoint,
144
+ })
145
+ }>
146
+ <Edit />
147
+ </IconButton>
148
+ <IconButton
149
+ kind="danger--ghost"
150
+ size={responsiveSize}
151
+ label={t('delete', 'Delete')}
152
+ onClick={() => showDeletePaymentPointModal(paymentPoint)}>
153
+ <TrashCan />
154
+ </IconButton>
155
+ </div>
156
+ </TableCell>
157
+ </TableRow>
158
+ );
159
+ })}
160
+ </TableBody>
161
+ </Table>
162
+ {tableRows.length === 0 ? (
163
+ <div className={styles.tileContainer}>
164
+ <Tile className={styles.tile}>
165
+ <div className={styles.tileContent}>
166
+ <p className={styles.content}>{t('noPaymentPointsFound', 'No payment points found')}</p>
167
+ <p className={styles.emptyStateHelperText}>
168
+ {t('checkFilters', 'Please check the filters above and try again')}
169
+ </p>
170
+ </div>
171
+ </Tile>
172
+ </div>
173
+ ) : null}
174
+ {tableRows.length > 0 && (
175
+ <Pagination
176
+ forwardText={t('nextPage', 'Next page')}
177
+ backwardText={t('previousPage', 'Previous page')}
178
+ page={currentPage}
179
+ pageSize={currentPageSize}
180
+ pageSizes={pageSizes}
181
+ totalItems={tableRows.length}
182
+ className={styles.pagination}
183
+ onChange={({ pageSize, page }) => {
184
+ if (pageSize !== currentPageSize) {
185
+ setCurrentPageSize(pageSize);
186
+ setCurrentPage(1);
187
+ }
188
+ if (page !== currentPage) {
189
+ setCurrentPage(page);
190
+ }
191
+ }}
192
+ />
193
+ )}
194
+ </TableContainer>
195
+ )}
196
+ </DataTable>
197
+ </Layer>
198
+ </div>
199
+ );
200
+ };
201
+
202
+ export default PaymentPointTable;
@@ -0,0 +1,16 @@
1
+ import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { PaymentPoint } from './type';
3
+ import useSWR from 'swr';
4
+
5
+ export const usePaymentPoints = () => {
6
+ const url = `${restBaseUrl}/cashier/cashPoint?v=full`;
7
+
8
+ const { data, isLoading, error, mutate } = useSWR<FetchResponse<{ results: Array<PaymentPoint> }>>(url, openmrsFetch);
9
+
10
+ return {
11
+ paymentPoints: data?.data?.results ?? [],
12
+ error,
13
+ isLoading,
14
+ mutate,
15
+ };
16
+ };
@@ -0,0 +1,39 @@
1
+ import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { type PaymentPoint } from '../type';
3
+ import { mutate } from 'swr';
4
+
5
+ export const createPaymentPoint = (payload: Partial<PaymentPoint>) => {
6
+ const url = `${restBaseUrl}/cashier/cashPoint?v=full`;
7
+ return openmrsFetch(url, {
8
+ method: 'POST',
9
+ body: payload,
10
+ headers: {
11
+ 'Content-Type': 'application/json',
12
+ },
13
+ });
14
+ };
15
+
16
+ export const updatePaymentPoint = (uuid: string, payload: Partial<PaymentPoint>) => {
17
+ const url = `${restBaseUrl}/cashier/cashPoint/${uuid}`;
18
+ return openmrsFetch(url, {
19
+ method: 'POST',
20
+ body: payload,
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ },
24
+ });
25
+ };
26
+
27
+ export const deletePaymentPoint = (uuid: string) => {
28
+ const url = `${restBaseUrl}/cashier/cashPoint/${uuid}`;
29
+ return openmrsFetch(url, {
30
+ method: 'DELETE',
31
+ headers: {
32
+ 'Content-Type': 'application/json',
33
+ },
34
+ });
35
+ };
36
+
37
+ export const handleMutation = (mutateUrl: string) => {
38
+ return mutate(mutateUrl);
39
+ };
@@ -0,0 +1,33 @@
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
+
5
+ .form {
6
+ display: flex;
7
+ flex-direction: column;
8
+ justify-content: space-between;
9
+ height: 100%;
10
+ }
11
+
12
+ .formContainer {
13
+ margin: layout.$spacing-05;
14
+ }
15
+
16
+ .tablet {
17
+ padding: layout.$spacing-06 layout.$spacing-05;
18
+ background-color: colors.$white;
19
+ }
20
+
21
+ .desktop {
22
+ padding: 0;
23
+ }
24
+
25
+ .paymentMethods {
26
+ display: flex;
27
+ flex-direction: column;
28
+ row-gap: layout.$spacing-05;
29
+ }
30
+
31
+ .formStackControl {
32
+ row-gap: layout.$layout-01;
33
+ }
@@ -0,0 +1,194 @@
1
+ import React, { useEffect } from 'react';
2
+ import { useTranslation } from 'react-i18next';
3
+ import {
4
+ DefaultWorkspaceProps,
5
+ ResponsiveWrapper,
6
+ restBaseUrl,
7
+ showSnackbar,
8
+ useLayoutType,
9
+ } from '@openmrs/esm-framework';
10
+ import { Controller, FormProvider, useForm } from 'react-hook-form';
11
+ import styles from './billing-payment-point.workspace.scss';
12
+ import { TextInput, ButtonSet, Button, InlineLoading, Stack, Toggle } from '@carbon/react';
13
+ import { z } from 'zod';
14
+ import { zodResolver } from '@hookform/resolvers/zod';
15
+ import classNames from 'classnames';
16
+ import { type PaymentPoint } from '../type';
17
+ import { createPaymentPoint, updatePaymentPoint, handleMutation } from './billing-payment-point.resource';
18
+
19
+ const paymentPointSchema = z.object({
20
+ name: z.string().min(1, { message: 'Payment point name is required' }),
21
+ description: z.string().min(1, { message: 'Description is required' }),
22
+ retired: z.boolean().optional().default(false),
23
+ location: z.string().optional().default(''),
24
+ });
25
+
26
+ type PaymentPointFormSchema = z.infer<typeof paymentPointSchema>;
27
+
28
+ type PaymentPointWorkspaceProps = DefaultWorkspaceProps & {
29
+ initialPaymentPoint?: PaymentPoint;
30
+ };
31
+
32
+ const PaymentPointWorkspace: React.FC<PaymentPointWorkspaceProps> = ({
33
+ closeWorkspace,
34
+ promptBeforeClosing,
35
+ closeWorkspaceWithSavedChanges,
36
+ initialPaymentPoint = {} as PaymentPoint,
37
+ }) => {
38
+ const { t } = useTranslation();
39
+ const isTablet = useLayoutType() === 'tablet';
40
+ const isEditMode = !!initialPaymentPoint?.uuid;
41
+
42
+ const formDefaultValues: PaymentPointFormSchema = isEditMode
43
+ ? {
44
+ name: initialPaymentPoint.name ?? '',
45
+ description: initialPaymentPoint.description ?? '',
46
+ retired: initialPaymentPoint.retired ?? false,
47
+ location: initialPaymentPoint.location?.uuid ?? '',
48
+ }
49
+ : {
50
+ name: '',
51
+ description: '',
52
+ retired: false,
53
+ location: '',
54
+ };
55
+
56
+ const formMethods = useForm<PaymentPointFormSchema>({
57
+ resolver: zodResolver(paymentPointSchema),
58
+ mode: 'all',
59
+ defaultValues: formDefaultValues,
60
+ });
61
+
62
+ const { errors, isSubmitting, isDirty } = formMethods.formState;
63
+
64
+ const onSubmit = async (data: PaymentPointFormSchema) => {
65
+ const payload: Partial<PaymentPoint> = {
66
+ name: data.name,
67
+ description: data.description,
68
+ retired: data.retired ?? false,
69
+ ...(data.location && { location: { uuid: data.location } as any }),
70
+ };
71
+
72
+ try {
73
+ const response = isEditMode
74
+ ? await updatePaymentPoint(initialPaymentPoint.uuid, payload)
75
+ : await createPaymentPoint(payload);
76
+
77
+ if (response.ok) {
78
+ const successMessage = isEditMode
79
+ ? t('paymentPointUpdated', 'Payment point updated successfully')
80
+ : t('paymentPointCreated', 'Payment point created successfully');
81
+
82
+ const successSubtitle = isEditMode
83
+ ? t('paymentPointUpdatedSubtitle', 'The payment point has been updated successfully')
84
+ : t('paymentPointCreatedSubtitle', 'The payment point has been created successfully');
85
+
86
+ showSnackbar({
87
+ title: successMessage,
88
+ subtitle: successSubtitle,
89
+ kind: 'success',
90
+ isLowContrast: true,
91
+ });
92
+
93
+ closeWorkspaceWithSavedChanges();
94
+ handleMutation(`${restBaseUrl}/cashier/cashPoint?v=full`);
95
+ }
96
+ } catch (error) {
97
+ const errorObject = error?.responseBody?.error;
98
+ const errorMessage = errorObject?.message ?? 'An error occurred while saving the payment point';
99
+
100
+ showSnackbar({
101
+ title: t('paymentPointSaveFailed', 'Payment point save failed'),
102
+ subtitle: t(
103
+ 'paymentPointSaveFailedSubtitle',
104
+ 'An error occurred while saving the payment point: {{errorMessage}}',
105
+ { errorMessage },
106
+ ),
107
+ kind: 'error',
108
+ isLowContrast: true,
109
+ });
110
+ }
111
+ };
112
+
113
+ const handleError = (errors) => {
114
+ showSnackbar({
115
+ title: t('paymentPointSaveFailed', 'Payment point save failed'),
116
+ subtitle: t('paymentPointSaveFailedSubtitle', 'Please check the form for errors'),
117
+ kind: 'error',
118
+ isLowContrast: true,
119
+ });
120
+ };
121
+
122
+ useEffect(() => {
123
+ if (isDirty) {
124
+ promptBeforeClosing(() => isDirty);
125
+ }
126
+ }, [isDirty, promptBeforeClosing]);
127
+
128
+ return (
129
+ <FormProvider {...formMethods}>
130
+ <form onSubmit={formMethods.handleSubmit(onSubmit, handleError)} className={styles.form}>
131
+ <div className={styles.formContainer}>
132
+ <Stack className={styles.formStackControl} gap={7}>
133
+ <ResponsiveWrapper>
134
+ <Controller
135
+ name="name"
136
+ control={formMethods.control}
137
+ render={({ field }) => (
138
+ <TextInput
139
+ {...field}
140
+ id="name"
141
+ type="text"
142
+ labelText={t('paymentPointName', 'Payment point name')}
143
+ placeholder={t('paymentPointNamePlaceholder', 'Enter payment point name')}
144
+ invalid={!!errors.name}
145
+ invalidText={errors.name?.message}
146
+ />
147
+ )}
148
+ />
149
+ </ResponsiveWrapper>
150
+
151
+ <ResponsiveWrapper>
152
+ <Controller
153
+ name="description"
154
+ control={formMethods.control}
155
+ render={({ field }) => (
156
+ <TextInput
157
+ {...field}
158
+ id="description"
159
+ type="text"
160
+ labelText={t('paymentPointDescription', 'Payment point description')}
161
+ placeholder={t('paymentPointDescriptionPlaceholder', 'Enter payment point description')}
162
+ invalid={!!errors.description}
163
+ invalidText={errors.description?.message}
164
+ />
165
+ )}
166
+ />
167
+ </ResponsiveWrapper>
168
+ </Stack>
169
+ </div>
170
+
171
+ <ButtonSet className={classNames({ [styles.tablet]: isTablet, [styles.desktop]: !isTablet })}>
172
+ <Button style={{ maxWidth: '50%' }} kind="secondary" onClick={() => closeWorkspace()}>
173
+ {t('cancel', 'Cancel')}
174
+ </Button>
175
+ <Button
176
+ disabled={isSubmitting || Object.keys(errors).length > 0}
177
+ style={{ maxWidth: '50%' }}
178
+ kind="primary"
179
+ type="submit">
180
+ {isSubmitting ? (
181
+ <span style={{ display: 'flex', justifyItems: 'center' }}>
182
+ {t('submitting', 'Submitting...')} <InlineLoading status="active" iconDescription="Loading" />
183
+ </span>
184
+ ) : (
185
+ t('saveAndClose', 'Save & close')
186
+ )}
187
+ </Button>
188
+ </ButtonSet>
189
+ </form>
190
+ </FormProvider>
191
+ );
192
+ };
193
+
194
+ export default PaymentPointWorkspace;
@@ -0,0 +1,35 @@
1
+ export interface PaymentPoint {
2
+ uuid: string;
3
+ name: string;
4
+ description: string;
5
+ retired: boolean;
6
+ retireReason: any;
7
+ auditInfo: AuditInfo;
8
+ location: Location;
9
+ resourceVersion: string;
10
+ }
11
+
12
+ export interface AuditInfo {
13
+ creator: Creator;
14
+ dateCreated: string;
15
+ changedBy: any;
16
+ dateChanged: any;
17
+ }
18
+
19
+ export interface Creator {
20
+ uuid: string;
21
+ display: string;
22
+ links: Array<Link>;
23
+ }
24
+
25
+ export interface Link {
26
+ rel: string;
27
+ uri: string;
28
+ resourceAlias: string;
29
+ }
30
+
31
+ export interface Location {
32
+ uuid: string;
33
+ display: string;
34
+ links: Array<Link>;
35
+ }
@@ -0,0 +1,15 @@
1
+ @use '@carbon/layout';
2
+ @use '@carbon/type';
3
+ @use '@carbon/colors';
4
+
5
+ .adminPage {
6
+ padding: layout.$spacing-05;
7
+ background: var(--cds-field);
8
+ }
9
+
10
+ :root {
11
+ --cds-border-interactive: #2e6b8a;
12
+ --cds-focus: none;
13
+ --cds-interactive: #2e6b8a;
14
+ --cds-link-primary: #2e6b8a;
15
+ }