@openmrs/esm-stock-management-app 3.0.1-pre.848 → 3.0.1-pre.853

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 (108) hide show
  1. package/.husky/pre-commit +4 -1
  2. package/__mocks__/react-i18next.js +8 -9
  3. package/dist/10.js +1 -1
  4. package/dist/10.js.map +1 -1
  5. package/dist/119.js +1 -1
  6. package/dist/119.js.map +1 -1
  7. package/dist/14.js +1 -1
  8. package/dist/14.js.map +1 -1
  9. package/dist/172.js +1 -1
  10. package/dist/172.js.map +1 -1
  11. package/dist/20.js +1 -1
  12. package/dist/20.js.map +1 -1
  13. package/dist/290.js +1 -1
  14. package/dist/290.js.map +1 -1
  15. package/dist/33.js +1 -0
  16. package/dist/33.js.map +1 -0
  17. package/dist/467.js +1 -1
  18. package/dist/467.js.map +1 -1
  19. package/dist/574.js +1 -1
  20. package/dist/606.js +1 -1
  21. package/dist/606.js.map +1 -1
  22. package/dist/642.js +1 -1
  23. package/dist/642.js.map +1 -1
  24. package/dist/675.js +1 -1
  25. package/dist/675.js.map +1 -1
  26. package/dist/727.js +1 -1
  27. package/dist/727.js.map +1 -1
  28. package/dist/842.js +1 -1
  29. package/dist/842.js.map +1 -1
  30. package/dist/93.js +1 -1
  31. package/dist/93.js.map +1 -1
  32. package/dist/main.js +1 -1
  33. package/dist/main.js.map +1 -1
  34. package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +70 -70
  35. package/dist/routes.json +1 -1
  36. package/jest.config.js +6 -3
  37. package/package.json +1 -1
  38. package/src/core/components/table/table.component.tsx +2 -2
  39. package/src/index.ts +5 -5
  40. package/src/stock-items/add-bulk-stock-item/add-stock-items-bulk-import-action-button.component.tsx +3 -3
  41. package/src/stock-items/add-bulk-stock-item/{stock-items-bulk-import.component.tsx → stock-items-bulk-import.modal.tsx} +20 -19
  42. package/src/stock-items/add-bulk-stock-item/stock-items-bulk-import.resource.ts +1 -1
  43. package/src/stock-items/add-bulk-stock-item/stock-items-bulk-import.test.tsx +59 -59
  44. package/src/stock-items/add-stock-item/add-stock-action-button.component.tsx +6 -6
  45. package/src/stock-items/add-stock-item/add-stock-item.component.tsx +6 -4
  46. package/src/stock-items/add-stock-item/add-stock-item.scss +5 -0
  47. package/src/stock-items/add-stock-item/add-stock-item.test.tsx +28 -43
  48. package/src/stock-items/add-stock-item/packaging-units/packaging-units-delete-modal.component.tsx +3 -4
  49. package/src/stock-items/add-stock-item/packaging-units/packaging-units.component.tsx +9 -10
  50. package/src/stock-items/add-stock-item/packaging-units/packaging-units.scss +4 -4
  51. package/src/stock-items/add-stock-item/stock-item-details/stock-item-details.component.tsx +27 -19
  52. package/src/stock-items/add-stock-item/stock-item-references/stock-item-references.scss +4 -4
  53. package/src/stock-items/add-stock-item/stock-item-rules/add-stock-rules.component.tsx +15 -9
  54. package/src/stock-items/add-stock-item/stock-item-rules/add-stock-rules.scss +1 -0
  55. package/src/stock-items/add-stock-item/stock-item-rules/delete-stock-rule-modal.component.tsx +2 -1
  56. package/src/stock-items/add-stock-item/stock-item-rules/stock-item-rules.component.tsx +14 -16
  57. package/src/stock-items/add-stock-item/stock-item-rules/stock-item-rules.scss +7 -3
  58. package/src/stock-items/add-stock-item/transactions/printout/transactions-print-bincard-preview.modal.tsx +14 -6
  59. package/src/stock-items/add-stock-item/transactions/printout/transactions-print-stockcard-preview.modal.tsx +14 -8
  60. package/src/stock-items/edit-stock-item/edit-stock-item-action-menu.component.tsx +2 -2
  61. package/src/stock-items/stock-item.utils.tsx +3 -5
  62. package/src/stock-items/stock-items-table.component.tsx +47 -45
  63. package/src/stock-items/stock-items-table.resource.ts +2 -2
  64. package/src/stock-items/stock-items-table.scss +5 -1
  65. package/src/stock-items/stock-items-table.test.tsx +106 -65
  66. package/src/stock-locations/location-admin-form.component.tsx +5 -4
  67. package/src/stock-locations/stock-locations-table.component.tsx +10 -8
  68. package/src/stock-lookups/stock-lookups.resource.ts +3 -2
  69. package/src/stock-operations/stock-operations-dialog/stock-operations-dialog.component.tsx +2 -2
  70. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.component.tsx +11 -11
  71. package/src/stock-operations/stock-operations-forms/input-components/batch-no-selector.test.tsx +115 -25
  72. package/src/stock-operations/stock-operations-forms/input-components/qty-uim-selector.test.tsx +107 -65
  73. package/src/stock-operations/stock-operations-forms/input-components/quantity-uom-selector.component.tsx +9 -9
  74. package/src/stock-operations/stock-operations-forms/input-components/stock-operation-reason-selector.test.tsx +35 -153
  75. package/src/stock-operations/stock-operations-forms/input-components/user-selector.test.tsx +82 -29
  76. package/src/stock-operations/stock-operations-forms/step1.test.tsx +204 -69
  77. package/src/stock-operations/stock-operations-forms/step2.test.tsx +140 -63
  78. package/src/stock-operations/stock-operations-forms/step3.test.tsx +79 -60
  79. package/src/stock-operations/stock-operations-forms/steps/stock-operation-items-form-step.component.tsx +6 -5
  80. package/src/stock-operations/stock-operations-forms/steps/stock-operation-submission-form-step.component.tsx +12 -11
  81. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.scss +1 -0
  82. package/src/stock-operations/stock-operations-forms/stock-item-form/stock-item-form.workspace.tsx +20 -12
  83. package/src/stock-operations/stock-operations-forms/stock-operation-form.scss +1 -0
  84. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stepper.scss +1 -3
  85. package/src/stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component.tsx +2 -1
  86. package/src/stock-reports/generate-report/create-stock-report.scss +3 -2
  87. package/src/stock-reports/generate-report/create-stock-report.workspace.tsx +32 -25
  88. package/src/stock-reports/report-list/stock-report-parameters.component.tsx +1 -1
  89. package/src/stock-reports/report-list/stock-report-status.component.tsx +1 -1
  90. package/src/stock-reports/report-list/stock-reports.component.tsx +24 -25
  91. package/src/stock-reports/report-list/stock-reports.scss +10 -2
  92. package/src/stock-sources/add-stock-sources/add-stock-sources.scss +11 -4
  93. package/src/stock-sources/add-stock-sources/add-stock-sources.test.tsx +38 -36
  94. package/src/stock-sources/add-stock-sources/add-stock-sources.workspace.tsx +35 -30
  95. package/src/stock-sources/delete-stock-modal.component.tsx +2 -1
  96. package/src/stock-sources/stock-sources-delete/stock-sources-delete.test.tsx +27 -36
  97. package/src/stock-sources/stock-sources-filter/stock-sources-filter.component.tsx +33 -21
  98. package/src/stock-sources/stock-sources-items-table.component.tsx +16 -17
  99. package/src/stock-sources/stock-sources-items-table.resource.ts +8 -6
  100. package/src/stock-sources/stock-sources-items-table.test.tsx +60 -37
  101. package/src/stock-sources/stock-sources.scss +6 -2
  102. package/src/stock-user-role-scopes/add-stock-user-scope/add-stock-user-role-scope.scss +5 -13
  103. package/src/stock-user-role-scopes/add-stock-user-scope/add-stock-user-role-scope.workspace.tsx +2 -2
  104. package/src/stock-user-role-scopes/delete-stock-user-scope-modal.component.tsx +2 -1
  105. package/translations/en.json +5 -6
  106. package/tsconfig.json +4 -0
  107. package/dist/627.js +0 -1
  108. package/dist/627.js.map +0 -1
@@ -1,105 +1,105 @@
1
1
  import React from 'react';
2
- import { render, screen, waitFor } from '@testing-library/react';
3
2
  import userEvent from '@testing-library/user-event';
4
- import ImportDialogPopup from './stock-items-bulk-import.component';
5
- import { showSnackbar } from '@openmrs/esm-framework';
6
- import { UploadStockItems } from './stock-items-bulk-import.resource';
3
+ import { render, screen } from '@testing-library/react';
4
+ import { type FetchResponse, showSnackbar } from '@openmrs/esm-framework';
5
+ import { uploadStockItems } from './stock-items-bulk-import.resource';
6
+ import ImportBulkStockItemsModal from './stock-items-bulk-import.modal';
7
7
 
8
- jest.mock('@openmrs/esm-framework', () => ({
9
- showSnackbar: jest.fn(),
10
- }));
8
+ const mockShowSnackbar = jest.mocked(showSnackbar);
9
+ const mockUploadStockItems = jest.mocked(uploadStockItems);
11
10
 
12
11
  jest.mock('./stock-items-bulk-import.resource', () => ({
13
- UploadStockItems: jest.fn(),
12
+ uploadStockItems: jest.fn(),
14
13
  }));
15
14
 
16
- describe('ImportDialogPopup', () => {
15
+ describe('ImportBulkStockItemsModal', () => {
17
16
  const mockCloseModal = jest.fn();
18
17
 
19
- beforeEach(() => {
20
- jest.clearAllMocks();
21
- });
22
-
23
18
  it('renders with initial state and UI elements', () => {
24
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
19
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
25
20
 
26
- expect(screen.getByText('Import Stock Items')).toBeInTheDocument();
27
- expect(screen.getByRole('button', { name: 'Select file' })).toBeInTheDocument();
28
- expect(screen.getByText('Only .csv files at 2mb or less')).toBeInTheDocument();
29
- expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
30
- expect(screen.getByRole('button', { name: 'Upload StockItems' })).toBeInTheDocument();
21
+ expect(screen.getByText(/import stock items/i)).toBeInTheDocument();
22
+ expect(screen.getByRole('button', { name: /select file/i })).toBeInTheDocument();
23
+ expect(screen.getByText(/only .csv files at 2mb or less/i)).toBeInTheDocument();
24
+ expect(screen.getByRole('button', { name: /cancel/i })).toBeInTheDocument();
25
+ expect(screen.getByRole('button', { name: /upload stock items/i })).toBeInTheDocument();
31
26
  });
32
27
 
33
28
  it('allows only CSV files', async () => {
34
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
29
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
35
30
 
36
- const fileInput = screen.getByLabelText('Select file') as HTMLInputElement;
31
+ const fileInput = screen.getByLabelText(/select file/i) as HTMLInputElement;
37
32
  expect(fileInput.accept).toBe('.csv');
38
33
  });
39
34
 
40
35
  it('closes modal when cancel button is clicked', async () => {
41
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
42
-
43
- const cancelButton = screen.getByRole('button', { name: 'Cancel' });
44
- await userEvent.click(cancelButton);
36
+ const user = userEvent.setup();
37
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
45
38
 
39
+ const cancelButton = screen.getByRole('button', { name: /cancel/i });
40
+ await user.click(cancelButton);
46
41
  expect(mockCloseModal).toHaveBeenCalledTimes(1);
47
42
  });
48
43
 
49
44
  it('does nothing when upload is clicked without a file', async () => {
50
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
45
+ const user = userEvent.setup();
46
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
51
47
 
52
- const uploadButton = screen.getByRole('button', { name: 'Upload StockItems' });
53
- await userEvent.click(uploadButton);
48
+ const uploadButton = screen.getByRole('button', { name: /upload stock items/i });
49
+ await user.click(uploadButton);
54
50
 
55
- expect(UploadStockItems).not.toHaveBeenCalled();
56
- expect(showSnackbar).not.toHaveBeenCalled();
51
+ expect(uploadStockItems).not.toHaveBeenCalled();
52
+ expect(mockShowSnackbar).not.toHaveBeenCalled();
57
53
  expect(mockCloseModal).not.toHaveBeenCalled();
58
54
  });
59
55
 
60
56
  it('uploads file successfully and shows success snackbar', async () => {
61
- (UploadStockItems as jest.Mock).mockResolvedValue({});
62
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
57
+ const user = userEvent.setup();
58
+
59
+ mockUploadStockItems.mockResolvedValue({
60
+ data: {},
61
+ status: 200,
62
+ ok: true,
63
+ } as FetchResponse<unknown>);
64
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
63
65
 
64
66
  const validFile = new File(['test content'], 'valid.csv', { type: 'text/csv' });
65
67
  const fileInput = screen.getByLabelText('Select file') as HTMLInputElement;
66
68
  await userEvent.upload(fileInput, validFile);
67
69
 
68
- const uploadButton = screen.getByRole('button', { name: 'Upload StockItems' });
69
- await userEvent.click(uploadButton);
70
-
71
- await waitFor(() => expect(UploadStockItems).toHaveBeenCalled());
72
- await waitFor(() =>
73
- expect(showSnackbar).toHaveBeenCalledWith(
74
- expect.objectContaining({
75
- kind: 'success',
76
- title: 'Uploaded Order',
77
- }),
78
- ),
70
+ const uploadButton = screen.getByRole('button', { name: /upload stock items/i });
71
+ await user.click(uploadButton);
72
+
73
+ expect(uploadStockItems).toHaveBeenCalled();
74
+ expect(mockShowSnackbar).toHaveBeenCalledWith(
75
+ expect.objectContaining({
76
+ kind: 'success',
77
+ title: 'Stock items uploaded successfully',
78
+ }),
79
79
  );
80
- await waitFor(() => expect(mockCloseModal).toHaveBeenCalledTimes(1));
80
+ expect(mockCloseModal).toHaveBeenCalledTimes(1);
81
81
  });
82
82
 
83
83
  it('shows error snackbar on upload failure', async () => {
84
- (UploadStockItems as jest.Mock).mockRejectedValue(new Error('Upload failed'));
85
- render(<ImportDialogPopup closeModal={mockCloseModal} />);
84
+ const user = userEvent.setup();
85
+
86
+ mockUploadStockItems.mockRejectedValue(new Error('Upload failed'));
87
+ render(<ImportBulkStockItemsModal closeModal={mockCloseModal} />);
86
88
 
87
89
  const validFile = new File(['test content'], 'valid.csv', { type: 'text/csv' });
88
- const fileInput = screen.getByLabelText('Select file') as HTMLInputElement;
90
+ const fileInput = screen.getByLabelText(/select file/i) as HTMLInputElement;
89
91
  await userEvent.upload(fileInput, validFile);
90
92
 
91
- const uploadButton = screen.getByRole('button', { name: 'Upload StockItems' });
92
- await userEvent.click(uploadButton);
93
-
94
- await waitFor(() => expect(UploadStockItems).toHaveBeenCalled());
95
- await waitFor(() =>
96
- expect(showSnackbar).toHaveBeenCalledWith(
97
- expect.objectContaining({
98
- kind: 'error',
99
- title: 'An error occurred uploading stock items',
100
- }),
101
- ),
93
+ const uploadButton = screen.getByRole('button', { name: /upload stock items/i });
94
+ await user.click(uploadButton);
95
+
96
+ expect(uploadStockItems).toHaveBeenCalled();
97
+ expect(mockShowSnackbar).toHaveBeenCalledWith(
98
+ expect.objectContaining({
99
+ kind: 'error',
100
+ title: 'An error occurred uploading stock items',
101
+ }),
102
102
  );
103
- await waitFor(() => expect(mockCloseModal).not.toHaveBeenCalled());
103
+ expect(mockCloseModal).not.toHaveBeenCalled();
104
104
  });
105
105
  });
@@ -1,18 +1,18 @@
1
- import { Button } from '@carbon/react';
2
1
  import React, { useCallback } from 'react';
3
2
  import { useTranslation } from 'react-i18next';
4
- import { launchAddOrStockItemWorkspace } from '../stock-item.utils';
3
+ import { Button } from '@carbon/react';
4
+ import { launchAddOrEditStockItemWorkspace } from '../stock-item.utils';
5
5
 
6
6
  const AddStockItemActionButton: React.FC = () => {
7
7
  const { t } = useTranslation();
8
8
 
9
- const handleClick = useCallback(() => {
10
- launchAddOrStockItemWorkspace(t);
9
+ const handleAddOrLaunchStockItemWorkspace = useCallback(() => {
10
+ launchAddOrEditStockItemWorkspace(t);
11
11
  }, [t]);
12
12
 
13
13
  return (
14
- <Button onClick={handleClick} size="md" kind="primary">
15
- {t('addNewStock', 'Add New')}
14
+ <Button onClick={handleAddOrLaunchStockItemWorkspace} size="md" kind="primary">
15
+ {t('addStockItem', 'Add stock item')}
16
16
  </Button>
17
17
  );
18
18
  };
@@ -1,15 +1,15 @@
1
- import { type DefaultWorkspaceProps } from '@openmrs/esm-framework';
2
1
  import React, { useState } from 'react';
3
2
  import { useTranslation } from 'react-i18next';
3
+ import { type DefaultWorkspaceProps } from '@openmrs/esm-framework';
4
4
  import { type StockItemDTO } from '../../core/api/types/stockItem/StockItem';
5
5
  import { type TabItem } from '../../core/components/tabs/types';
6
- import StockOperationStepper from '../../stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component';
7
6
  import BatchInformation from './batch-information/batch-information.component';
8
7
  import PackagingUnits from './packaging-units/packaging-units.component';
9
- import StockQuantities from './quantities/quantities.component';
10
8
  import StockItemDetails from './stock-item-details/stock-item-details.component';
11
- import StockReferences from './stock-item-references/stock-item-references.component';
12
9
  import StockItemRules from './stock-item-rules/stock-item-rules.component';
10
+ import StockOperationStepper from '../../stock-operations/stock-operations-forms/stock-operation-stepper/stock-operation-stepper.component';
11
+ import StockQuantities from './quantities/quantities.component';
12
+ import StockReferences from './stock-item-references/stock-item-references.component';
13
13
  import Transactions from './transactions/transactions.component';
14
14
 
15
15
  interface AddStockItemProps extends Partial<DefaultWorkspaceProps> {
@@ -20,9 +20,11 @@ const AddEditStockItem: React.FC<AddStockItemProps> = ({ stockItem, closeWorkspa
20
20
  const { t } = useTranslation();
21
21
  const [selectedTab, setSelectedTab] = useState(0);
22
22
  const isEditing = Boolean(stockItem);
23
+
23
24
  const handleTabChange = (index: number) => {
24
25
  setSelectedTab(index);
25
26
  };
27
+
26
28
  const tabs: TabItem[] = [
27
29
  {
28
30
  name: t('stockItemDetails', 'Stock Item Details'),
@@ -11,6 +11,7 @@
11
11
  }
12
12
 
13
13
  .button {
14
+ height: layout.$spacing-10;
14
15
  display: flex;
15
16
  align-content: flex-start;
16
17
  align-items: baseline;
@@ -22,3 +23,7 @@
22
23
  justify-content: space-between;
23
24
  width: 100%;
24
25
  }
26
+
27
+ .stack {
28
+ margin: layout.$spacing-05;
29
+ }
@@ -1,22 +1,8 @@
1
1
  import React from 'react';
2
- import { render, screen } from '@testing-library/react';
3
2
  import userEvent from '@testing-library/user-event';
4
- import AddEditStockItem from './add-stock-item.component';
3
+ import { render, screen } from '@testing-library/react';
5
4
  import { type StockItemDTO } from '../../core/api/types/stockItem/StockItem';
6
-
7
- // Mock components
8
- jest.mock('react-i18next', () => ({
9
- useTranslation: () => ({
10
- t: (key) => key,
11
- }),
12
- }));
13
- jest.mock('@carbon/react', () => {
14
- const originalModule = jest.requireActual('@carbon/react');
15
- return {
16
- ...originalModule,
17
- useMatchMedia: jest.fn().mockReturnValue(false),
18
- };
19
- });
5
+ import AddEditStockItem from './add-stock-item.component';
20
6
 
21
7
  jest.mock('@carbon/react/icons', () => ({
22
8
  Save: () => <div>Save Icon</div>,
@@ -129,11 +115,11 @@ describe('AddEditStockItem', () => {
129
115
  const user = userEvent.setup();
130
116
  render(<AddEditStockItem stockItem={mockModel} />);
131
117
 
132
- await user.click(screen.getByText('packagingUnits'));
118
+ await user.click(screen.getByText(/packaging units/i));
133
119
  expect(screen.getByTestId('packaging-units')).toBeInTheDocument();
134
120
  expect(screen.getByText('Packaging Units: test-uuid-123')).toBeInTheDocument();
135
121
 
136
- await user.click(screen.getByText('transactions'));
122
+ await user.click(screen.getByText(/transactions/i));
137
123
  expect(screen.getByTestId('transactions')).toBeInTheDocument();
138
124
  expect(screen.getByText('Transactions: test-uuid-123')).toBeInTheDocument();
139
125
  });
@@ -142,12 +128,12 @@ describe('AddEditStockItem', () => {
142
128
  render(<AddEditStockItem />);
143
129
 
144
130
  const disabledTabs = [
145
- 'packagingUnits',
146
- 'transactions',
147
- 'batchInformation',
148
- 'quantities',
149
- 'stockRules',
150
- 'references',
131
+ /batch information/i,
132
+ /packaging units/i,
133
+ /quantities/i,
134
+ /references/i,
135
+ /rules/i,
136
+ /transactions/i,
151
137
  ];
152
138
  disabledTabs.forEach((tabName) => {
153
139
  const tab = screen.getByRole('button', { name: tabName });
@@ -159,12 +145,12 @@ describe('AddEditStockItem', () => {
159
145
  render(<AddEditStockItem stockItem={mockModel} />);
160
146
 
161
147
  const enabledTabs = [
162
- 'packagingUnits',
163
- 'transactions',
164
- 'batchInformation',
165
- 'quantities',
166
- 'stockRules',
167
- 'references',
148
+ /batch information/i,
149
+ /packaging units/i,
150
+ /quantities/i,
151
+ /references/i,
152
+ /rules/i,
153
+ /transactions/i,
168
154
  ];
169
155
  enabledTabs.forEach((tabName) => {
170
156
  const tab = screen.getByRole('button', { name: tabName });
@@ -178,22 +164,22 @@ describe('AddEditStockItem', () => {
178
164
 
179
165
  expect(screen.getByText('Stock Item Details: test-uuid-123')).toBeInTheDocument();
180
166
 
181
- await user.click(screen.getByText('packagingUnits'));
167
+ await user.click(screen.getByText(/packaging units/i));
182
168
  expect(screen.getByText('Packaging Units: test-uuid-123')).toBeInTheDocument();
183
169
 
184
- await user.click(screen.getByText('transactions'));
170
+ await user.click(screen.getByText(/transactions/i));
185
171
  expect(screen.getByText('Transactions: test-uuid-123')).toBeInTheDocument();
186
172
 
187
- await user.click(screen.getByText('batchInformation'));
173
+ await user.click(screen.getByText(/batch information/i));
188
174
  expect(screen.getByText('Batch Information: test-uuid-123')).toBeInTheDocument();
189
175
 
190
- await user.click(screen.getByText('quantities'));
176
+ await user.click(screen.getByText(/quantities/i));
191
177
  expect(screen.getByText('Quantities: test-uuid-123')).toBeInTheDocument();
192
178
 
193
- await user.click(screen.getByText('stockRules'));
179
+ await user.click(screen.getByText(/rules/i));
194
180
  expect(screen.getByText('Rules: test-uuid-123')).toBeInTheDocument();
195
181
 
196
- await user.click(screen.getByText('references'));
182
+ await user.click(screen.getByText(/references/i));
197
183
  expect(screen.getByText('References: test-uuid-123')).toBeInTheDocument();
198
184
  });
199
185
 
@@ -201,13 +187,12 @@ describe('AddEditStockItem', () => {
201
187
  render(<AddEditStockItem stockItem={mockModel} />);
202
188
 
203
189
  const tabNames = [
204
- 'stockItemDetails',
205
- 'packagingUnits',
206
- 'transactions',
207
- 'batchInformation',
208
- 'quantities',
209
- 'stockRules',
210
- 'references',
190
+ /batch information/i,
191
+ /packaging units/i,
192
+ /quantities/i,
193
+ /references/i,
194
+ /rules/i,
195
+ /transactions/i,
211
196
  ];
212
197
  tabNames.forEach((tabName) => {
213
198
  expect(screen.getByText(tabName)).toBeInTheDocument();
@@ -1,11 +1,10 @@
1
1
  import React, { useState, useEffect } from 'react';
2
- import { showSnackbar } from '@openmrs/esm-framework';
2
+ import { useTranslation } from 'react-i18next';
3
3
  import { Button, ModalHeader, ModalBody, ModalFooter, TextArea } from '@carbon/react';
4
+ import { showSnackbar, getCoreTranslation } from '@openmrs/esm-framework';
5
+ import { type StockItemPackagingUOMDTO } from '../../../core/api/types/stockItem/StockItemPackagingUOM';
4
6
  import { deleteStockItemPackagingUnit } from '../../stock-items.resource';
5
7
  import { useStockItemPackageUnitsHook } from './packaging-units.resource';
6
- import { useTranslation } from 'react-i18next';
7
- import { type StockItemPackagingUOMDTO } from '../../../core/api/types/stockItem/StockItemPackagingUOM';
8
-
9
8
  import styles from '../packaging-units/packaging-units.scss';
10
9
 
11
10
  interface DeletePackagingUnitProps {
@@ -1,3 +1,4 @@
1
+ import React, { useEffect, useMemo, useState } from 'react';
1
2
  import {
2
3
  Button,
3
4
  DataTable,
@@ -12,19 +13,17 @@ import {
12
13
  } from '@carbon/react';
13
14
  import { Save } from '@carbon/react/icons';
14
15
  import { zodResolver } from '@hookform/resolvers/zod';
15
- import { restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
16
- import React, { useEffect, useMemo, useState } from 'react';
17
16
  import { FormProvider, useForm, useFormContext } from 'react-hook-form';
18
17
  import { useTranslation } from 'react-i18next';
18
+ import { getCoreTranslation, restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
19
+ import { createStockItemPackagingUnit, updateStockItemPackagingUnit } from '../../stock-items.resource';
20
+ import { handleMutate } from '../../../utils';
21
+ import { type PackageUnitFormData, packageUnitSchema } from './validationSchema';
19
22
  import { type StockItemPackagingUOMDTO } from '../../../core/api/types/stockItem/StockItemPackagingUOM';
23
+ import { useStockItemPackageUnitsHook } from './packaging-units.resource';
20
24
  import ControlledNumberInput from '../../../core/components/carbon/controlled-number-input/controlled-number-input.component';
21
- import { handleMutate } from '../../../utils';
22
- import { createStockItemPackagingUnit, updateStockItemPackagingUnit } from '../../stock-items.resource';
23
- import PackagingUnitsConceptSelector from '../packaging-units-concept-selector/packaging-units-concept-selector.component';
24
25
  import DeleteModalButton from './packaging-units-delete-modal-button.component';
25
- import { useStockItemPackageUnitsHook } from './packaging-units.resource';
26
- import { type PackageUnitFormData, packageUnitSchema } from './validationSchema';
27
-
26
+ import PackagingUnitsConceptSelector from '../packaging-units-concept-selector/packaging-units-concept-selector.component';
28
27
  import styles from './packaging-units.scss';
29
28
 
30
29
  interface PackagingUnitsProps {
@@ -236,7 +235,7 @@ const PackagingUnits: React.FC<PackagingUnitsProps> = ({ stockItemUuid, handleTa
236
235
  />
237
236
  <div className={styles.packageUnitsBtn}>
238
237
  <Button kind="secondary" onClick={handleCancelPackagingUnits}>
239
- {t('cancel', 'Cancel')}
238
+ {getCoreTranslation('cancel')}
240
239
  </Button>
241
240
  <Button
242
241
  name="save"
@@ -246,7 +245,7 @@ const PackagingUnits: React.FC<PackagingUnitsProps> = ({ stockItemUuid, handleTa
246
245
  kind="primary"
247
246
  renderIcon={Save}
248
247
  >
249
- {t('save', 'Save')}
248
+ {getCoreTranslation('save')}
250
249
  </Button>
251
250
  </div>
252
251
  </FormProvider>
@@ -1,6 +1,6 @@
1
- @use '@carbon/styles/scss/colors';
2
- @use '@carbon/styles/scss/spacing';
3
- @use '@carbon/styles/scss/type';
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
4
 
5
5
  .packingTable {
6
6
  min-height: 15rem;
@@ -13,7 +13,7 @@
13
13
  }
14
14
 
15
15
  .packagingTableBody {
16
- min-height: spacing.$spacing-13;
16
+ min-height: layout.$spacing-13;
17
17
  }
18
18
 
19
19
  .packagingTableContainer {
@@ -1,26 +1,27 @@
1
- import { Button, ButtonSet, FormGroup, InlineLoading } from '@carbon/react';
1
+ import React, { forwardRef, useMemo } from 'react';
2
+ import classNames from 'classnames';
3
+ import { Button, ButtonSet, FormGroup, InlineLoading, Stack } from '@carbon/react';
2
4
  import { Save } from '@carbon/react/icons';
3
5
  import { zodResolver } from '@hookform/resolvers/zod';
4
- import { restBaseUrl, showSnackbar } from '@openmrs/esm-framework';
5
- import React, { forwardRef, useMemo } from 'react';
6
6
  import { type SubmitHandler, useForm } from 'react-hook-form';
7
7
  import { useTranslation } from 'react-i18next';
8
+ import { getCoreTranslation, restBaseUrl, showSnackbar, useLayoutType } from '@openmrs/esm-framework';
9
+ import { createStockItem, updateStockItem } from '../../stock-items.resource';
10
+ import { expirationOptions, radioOptions, StockItemType } from './stock-item-details.resource';
11
+ import { handleMutate } from '../../../utils';
12
+ import { launchAddOrEditStockItemWorkspace } from '../../stock-item.utils';
13
+ import { stockItemDetailsSchema, type StockItemFormData } from '../../validationSchema';
8
14
  import { type StockItemDTO } from '../../../core/api/types/stockItem/StockItem';
15
+ import ConceptsSelector from '../concepts-selector/concepts-selector.component';
9
16
  import ControlledNumberInput from '../../../core/components/carbon/controlled-number-input/controlled-number-input.component';
10
17
  import ControlledRadioButtonGroup from '../../../core/components/carbon/controlled-radio-button-group/controlled-radio-button-group.component';
11
18
  import ControlledTextInput from '../../../core/components/carbon/controlled-text-input/controlled-text-input.component';
12
- import { handleMutate } from '../../../utils';
13
- import styles from '../../add-stock-item/add-stock-item.scss';
14
- import { launchAddOrStockItemWorkspace } from '../../stock-item.utils';
15
- import { createStockItem, updateStockItem } from '../../stock-items.resource';
16
- import { stockItemDetailsSchema, type StockItemFormData } from '../../validationSchema';
17
- import ConceptsSelector from '../concepts-selector/concepts-selector.component';
18
19
  import DispensingUnitSelector from '../dispensing-unit-selector/dispensing-unit-selector.component';
19
20
  import DrugSelector from '../drug-selector/drug-selector.component';
20
21
  import PreferredVendorSelector from '../preferred-vendor-selector/preferred-vendor-selector.component';
21
22
  import StockItemCategorySelector from '../stock-item-category-selector/stock-item-category-selector.component';
22
23
  import StockItemUnitsEdit from '../stock-item-units-edit/stock-item-units-edit.component';
23
- import { expirationOptions, radioOptions, StockItemType } from './stock-item-details.resource';
24
+ import styles from '../../add-stock-item/add-stock-item.scss';
24
25
 
25
26
  interface StockItemDetailsProps {
26
27
  stockItem?: StockItemDTO;
@@ -31,6 +32,8 @@ interface StockItemDetailsProps {
31
32
  const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
32
33
  ({ stockItem, handleTabChange, onCloseWorkspace }) => {
33
34
  const { t } = useTranslation();
35
+ const isTablet = useLayoutType() === 'tablet';
36
+
34
37
  const { handleSubmit, control, formState, watch } = useForm<StockItemFormData>({
35
38
  defaultValues: stockItem ?? {},
36
39
  mode: 'all',
@@ -57,7 +60,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
57
60
  // launch edit dialog
58
61
  const item = response.data;
59
62
  item.isDrug = !!item.drugUuid;
60
- launchAddOrStockItemWorkspace(t, item);
63
+ launchAddOrEditStockItemWorkspace(t, item);
61
64
  }
62
65
  }
63
66
 
@@ -84,7 +87,7 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
84
87
 
85
88
  return (
86
89
  <form className={styles.formContainer}>
87
- <div>
90
+ <Stack className={styles.stack} gap={5}>
88
91
  {!stockItem && (
89
92
  <FormGroup
90
93
  className="clear-margin-bottom"
@@ -227,20 +230,25 @@ const StockItemDetails = forwardRef<never, StockItemDetailsProps>(
227
230
  {observableIsDrug && stockItem && (
228
231
  <StockItemUnitsEdit control={control} formState={formState} stockItemUuid={stockItem?.uuid} />
229
232
  )}
230
- </div>
231
- <ButtonSet className={styles.buttonSet}>
233
+ </Stack>
234
+ <ButtonSet
235
+ className={classNames(styles.buttonSet, {
236
+ [styles.tablet]: isTablet,
237
+ [styles.desktop]: !isTablet,
238
+ })}
239
+ >
232
240
  <Button kind="secondary" onClick={onCloseWorkspace} className={styles.button}>
233
- {t('cancel', 'Cancel')}
241
+ {getCoreTranslation('cancel')}
234
242
  </Button>
235
243
  <Button
236
- name="save"
237
- type="button"
238
244
  className={styles.button}
239
- onClick={handleSubmit(handleSave)}
240
245
  kind="primary"
246
+ name="save"
247
+ onClick={handleSubmit(handleSave)}
241
248
  renderIcon={Save}
249
+ type="button"
242
250
  >
243
- {formState.isSubmitting ? <InlineLoading /> : t('save', 'Save')}
251
+ {formState.isSubmitting ? <InlineLoading /> : getCoreTranslation('save')}
244
252
  </Button>
245
253
  </ButtonSet>
246
254
  </form>
@@ -1,6 +1,6 @@
1
- @use '@carbon/styles/scss/colors';
2
- @use '@carbon/styles/scss/spacing';
3
- @use '@carbon/styles/scss/type';
1
+ @use '@carbon/colors';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/type';
4
4
 
5
5
  .referencesTable {
6
6
  min-height: 15rem;
@@ -13,7 +13,7 @@
13
13
  }
14
14
 
15
15
  .referencesTableBody {
16
- min-height: spacing.$spacing-13;
16
+ min-height: layout.$spacing-13;
17
17
  }
18
18
 
19
19
  .referencesTableContainer {
@@ -1,3 +1,6 @@
1
+ import React, { type ChangeEvent, useCallback, useEffect, useState } from 'react';
2
+ import classNames from 'classnames';
3
+ import { useTranslation } from 'react-i18next';
1
4
  import {
2
5
  Button,
3
6
  ButtonSet,
@@ -9,15 +12,13 @@ import {
9
12
  SelectItem,
10
13
  TextInput,
11
14
  } from '@carbon/react';
12
- import { type DefaultWorkspaceProps, showSnackbar } from '@openmrs/esm-framework';
13
- import React, { type ChangeEvent, useCallback, useEffect, useState } from 'react';
14
- import { useTranslation } from 'react-i18next';
15
+ import { type DefaultWorkspaceProps, getCoreTranslation, showSnackbar, useLayoutType } from '@openmrs/esm-framework';
16
+ import { createOrUpdateStockRule } from './stock-rules.resource';
15
17
  import { ResourceRepresentation } from '../../../core/api/api';
18
+ import { type StockItemInventoryFilter, useStockItemPackagingUOMs } from '../../stock-items.resource';
16
19
  import { type StockRule } from '../../../core/api/types/stockItem/StockRule';
17
20
  import { useRoles, useStockTagLocations } from '../../../stock-lookups/stock-lookups.resource';
18
- import { type StockItemInventoryFilter, useStockItemPackagingUOMs } from '../../stock-items.resource';
19
21
  import styles from './add-stock-rules.scss';
20
- import { createOrUpdateStockRule } from './stock-rules.resource';
21
22
 
22
23
  interface AddStockRuleProps extends Partial<DefaultWorkspaceProps> {
23
24
  model?: StockRule;
@@ -26,7 +27,7 @@ interface AddStockRuleProps extends Partial<DefaultWorkspaceProps> {
26
27
 
27
28
  const StockRulesAddOrUpdate: React.FC<AddStockRuleProps> = ({ model, stockItemUuid, closeWorkspace }) => {
28
29
  const { t } = useTranslation();
29
-
30
+ const isTablet = useLayoutType() === 'tablet';
30
31
  const [stockItemFilter, setStockItemFilter] = useState<StockItemInventoryFilter>({
31
32
  startIndex: 0,
32
33
  v: ResourceRepresentation.Default,
@@ -315,12 +316,17 @@ const StockRulesAddOrUpdate: React.FC<AddStockRuleProps> = ({ model, stockItemUu
315
316
  notification will only be sent once per specified notification frequency.
316
317
  </div>
317
318
  </div>
318
- <ButtonSet className={styles.buttonSet}>
319
+ <ButtonSet
320
+ className={classNames(styles.buttonSet, {
321
+ [styles.tablet]: isTablet,
322
+ [styles.desktop]: !isTablet,
323
+ })}
324
+ >
319
325
  <Button kind="secondary" onClick={closeWorkspace} className={styles.button}>
320
- {t('cancel', 'Cancel')}
326
+ {getCoreTranslation('cancel')}
321
327
  </Button>
322
328
  <Button type="submit" className={styles.button}>
323
- {t('save', 'Save')}
329
+ {getCoreTranslation('save')}
324
330
  </Button>
325
331
  </ButtonSet>
326
332
  </Form>
@@ -12,6 +12,7 @@
12
12
  }
13
13
 
14
14
  .button {
15
+ height: layout.$spacing-10;
15
16
  display: flex;
16
17
  align-content: flex-start;
17
18
  align-items: baseline;
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  import { useTranslation } from 'react-i18next';
3
3
  import { Button, ModalHeader, ModalBody, ModalFooter } from '@carbon/react';
4
+ import { getCoreTranslation } from '@openmrs/esm-framework';
4
5
  import styles from '../../../root.scss';
5
6
 
6
7
  interface DeleteConfirmationProps {
@@ -30,7 +31,7 @@ const DeleteConfirmation: React.FC<DeleteConfirmationProps> = ({ close, onConfir
30
31
  </ModalBody>
31
32
  <ModalFooter>
32
33
  <Button size="lg" kind="secondary" onClick={handleCancel}>
33
- {t('cancel', 'Cancel')}
34
+ {getCoreTranslation('cancel')}
34
35
  </Button>
35
36
  <Button autoFocus kind="danger" onClick={handleDelete} size="lg">
36
37
  {t('delete', 'Delete')}