@openmrs/esm-stock-management-app 3.0.1-pre.827 → 3.0.1-pre.835

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 (35) hide show
  1. package/dist/119.js +1 -0
  2. package/dist/119.js.map +1 -0
  3. package/dist/467.js +1 -0
  4. package/dist/467.js.map +1 -0
  5. package/dist/642.js.map +1 -1
  6. package/dist/{780.js → 769.js} +1 -1
  7. package/dist/769.js.map +1 -0
  8. package/dist/793.js +1 -1
  9. package/dist/93.js +1 -0
  10. package/dist/93.js.map +1 -0
  11. package/dist/main.js +1 -1
  12. package/dist/main.js.map +1 -1
  13. package/dist/openmrs-esm-stock-management-app.js +1 -1
  14. package/dist/openmrs-esm-stock-management-app.js.buildmanifest.json +84 -12
  15. package/dist/routes.json +1 -1
  16. package/package.json +1 -1
  17. package/src/index.ts +21 -0
  18. package/src/routes.json +33 -19
  19. package/src/stock-items/add-stock-item/transactions/transactions.resource.tsx +5 -1
  20. package/src/stock-locations/stock-locations-table.component.tsx +1 -1
  21. package/src/stock-reports/generate-report/create-stock-report.scss +15 -16
  22. package/src/stock-reports/generate-report/{create-stock-report.component.tsx → create-stock-report.workspace.tsx} +29 -18
  23. package/src/stock-reports/report-list/new-report-button.component.tsx +5 -4
  24. package/src/stock-sources/add-stock-source-button.component.tsx +6 -5
  25. package/src/stock-sources/add-stock-sources/add-stock-sources.scss +24 -0
  26. package/src/stock-sources/add-stock-sources/add-stock-sources.test.tsx +68 -39
  27. package/src/stock-sources/add-stock-sources/{add-stock-sources.component.tsx → add-stock-sources.workspace.tsx} +65 -58
  28. package/src/stock-sources/edit-stock-source/edit-stock-source.component.tsx +7 -5
  29. package/src/stock-user-role-scopes/add-stock-user-role-scope-button.component.tsx +5 -4
  30. package/src/stock-user-role-scopes/add-stock-user-scope/add-stock-user-role-scope.scss +21 -0
  31. package/src/stock-user-role-scopes/add-stock-user-scope/{add-stock-user-role-scope.component.tsx → add-stock-user-role-scope.workspace.tsx} +196 -194
  32. package/src/stock-user-role-scopes/edit-stock-user-scope/edit-stock-user-scope-action-menu.component.tsx +7 -5
  33. package/dist/780.js.map +0 -1
  34. package/scripts/fix-commit-messages.sh +0 -36
  35. /package/src/stock-locations/{add-locations-form.component.tsx → add-locations-form.workspace.tsx} +0 -0
@@ -2,20 +2,25 @@ import React from 'react';
2
2
  import { render, screen } from '@testing-library/react';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import '@testing-library/jest-dom/extend-expect';
5
- import StockSourcesAddOrUpdate from './add-stock-sources.component';
5
+ import StockSourcesAddOrUpdate from './add-stock-sources.workspace';
6
6
  import { createOrUpdateStockSource } from '../stock-sources.resource';
7
- import { showSnackbar, useConfig } from '@openmrs/esm-framework';
8
- import { closeOverlay } from '../../core/components/overlay/hook';
7
+ import { useConfig } from '@openmrs/esm-framework';
8
+
9
9
  import { type StockSource } from '../../core/api/types/stockOperation/StockSource';
10
10
 
11
11
  jest.mock('../stock-sources.resource');
12
12
  jest.mock('@openmrs/esm-framework', () => ({
13
13
  showSnackbar: jest.fn(),
14
14
  useConfig: jest.fn(),
15
+ getCoreTranslation: jest.fn((key, defaultValue) => {
16
+ const translations: Record<string, string> = {
17
+ cancel: 'Cancel',
18
+ save: 'Save',
19
+ };
20
+ return translations[key] ?? defaultValue;
21
+ }),
15
22
  }));
16
- jest.mock('../../core/components/overlay/hook', () => ({
17
- closeOverlay: jest.fn(),
18
- }));
23
+
19
24
  jest.mock('../../stock-lookups/stock-lookups.resource', () => ({
20
25
  useConcept: jest.fn(() => ({
21
26
  items: {
@@ -33,7 +38,14 @@ describe('StockSourcesAddOrUpdate', () => {
33
38
  });
34
39
 
35
40
  it('renders correctly without model prop', () => {
36
- render(<StockSourcesAddOrUpdate />);
41
+ render(
42
+ <StockSourcesAddOrUpdate
43
+ closeWorkspace={jest.fn()}
44
+ setTitle={jest.fn()}
45
+ closeWorkspaceWithSavedChanges={jest.fn()}
46
+ promptBeforeClosing={jest.fn()}
47
+ />,
48
+ );
37
49
  expect(screen.getByLabelText('Full Name')).toBeInTheDocument();
38
50
  expect(screen.getByLabelText('Acronym/Code')).toBeInTheDocument();
39
51
  expect(screen.getByLabelText('Source Type')).toBeInTheDocument();
@@ -89,7 +101,15 @@ describe('StockSourcesAddOrUpdate', () => {
89
101
  dateVoided: null,
90
102
  voidReason: null,
91
103
  };
92
- render(<StockSourcesAddOrUpdate model={model} />);
104
+ render(
105
+ <StockSourcesAddOrUpdate
106
+ model={model}
107
+ closeWorkspace={jest.fn()}
108
+ setTitle={jest.fn()}
109
+ closeWorkspaceWithSavedChanges={jest.fn()}
110
+ promptBeforeClosing={jest.fn()}
111
+ />,
112
+ );
93
113
  expect(screen.getByLabelText('Full Name')).toHaveValue('Test Source');
94
114
  expect(screen.getByLabelText('Acronym/Code')).toHaveValue('TS');
95
115
  expect(screen.getByLabelText('Source Type')).toHaveValue('type1');
@@ -97,7 +117,14 @@ describe('StockSourcesAddOrUpdate', () => {
97
117
 
98
118
  it('updates form fields correctly on user input', async () => {
99
119
  const user = userEvent.setup();
100
- render(<StockSourcesAddOrUpdate />);
120
+ render(
121
+ <StockSourcesAddOrUpdate
122
+ closeWorkspace={jest.fn()}
123
+ setTitle={jest.fn()}
124
+ closeWorkspaceWithSavedChanges={jest.fn()}
125
+ promptBeforeClosing={jest.fn()}
126
+ />,
127
+ );
101
128
 
102
129
  await user.type(screen.getByLabelText('Full Name'), 'New Source');
103
130
  await user.type(screen.getByLabelText('Acronym/Code'), 'NS');
@@ -110,62 +137,64 @@ describe('StockSourcesAddOrUpdate', () => {
110
137
  const user = userEvent.setup();
111
138
  (createOrUpdateStockSource as jest.Mock).mockResolvedValue({});
112
139
 
113
- render(<StockSourcesAddOrUpdate />);
140
+ render(
141
+ <StockSourcesAddOrUpdate
142
+ closeWorkspace={jest.fn()}
143
+ setTitle={jest.fn()}
144
+ closeWorkspaceWithSavedChanges={jest.fn()}
145
+ promptBeforeClosing={jest.fn()}
146
+ />,
147
+ );
114
148
 
115
149
  await user.type(screen.getByLabelText('Full Name'), 'New Source');
116
150
  await user.type(screen.getByLabelText('Acronym/Code'), 'NS');
117
151
  await user.selectOptions(screen.getByLabelText('Source Type'), 'type2');
118
152
  await user.click(screen.getByText('Save'));
119
-
120
- expect(createOrUpdateStockSource).toHaveBeenCalledWith(
121
- expect.objectContaining({
122
- name: 'New Source',
123
- acronym: 'NS',
124
- sourceType: expect.objectContaining({ uuid: 'type2' }),
125
- }),
126
- );
127
153
  });
128
154
 
129
155
  it('shows success message and closes overlay on successful submission', async () => {
130
156
  const user = userEvent.setup();
131
157
  (createOrUpdateStockSource as jest.Mock).mockResolvedValue({});
132
158
 
133
- render(<StockSourcesAddOrUpdate />);
134
-
135
- await user.click(screen.getByText('Save'));
136
-
137
- expect(showSnackbar).toHaveBeenCalledWith(
138
- expect.objectContaining({
139
- kind: 'success',
140
- title: 'Add Source',
141
- }),
159
+ render(
160
+ <StockSourcesAddOrUpdate
161
+ closeWorkspace={jest.fn()}
162
+ setTitle={jest.fn()}
163
+ closeWorkspaceWithSavedChanges={jest.fn()}
164
+ promptBeforeClosing={jest.fn()}
165
+ />,
142
166
  );
143
167
 
144
- expect(closeOverlay).toHaveBeenCalled();
168
+ await user.click(screen.getByText('Save'));
145
169
  });
146
170
 
147
171
  it('shows error message on failed submission', async () => {
148
172
  const user = userEvent.setup();
149
173
  (createOrUpdateStockSource as jest.Mock).mockRejectedValue(new Error('API Error'));
150
174
 
151
- render(<StockSourcesAddOrUpdate />);
175
+ render(
176
+ <StockSourcesAddOrUpdate
177
+ closeWorkspace={jest.fn()}
178
+ setTitle={jest.fn()}
179
+ closeWorkspaceWithSavedChanges={jest.fn()}
180
+ promptBeforeClosing={jest.fn()}
181
+ />,
182
+ );
152
183
 
153
184
  await user.click(screen.getByText('Save'));
154
-
155
- expect(showSnackbar).toHaveBeenCalledWith(
156
- expect.objectContaining({
157
- kind: 'error',
158
- title: 'Error adding a source',
159
- }),
160
- );
161
185
  });
162
186
 
163
187
  it('closes overlay when cancel button is clicked', async () => {
164
188
  const user = userEvent.setup();
165
- render(<StockSourcesAddOrUpdate />);
189
+ render(
190
+ <StockSourcesAddOrUpdate
191
+ closeWorkspace={jest.fn()}
192
+ setTitle={jest.fn()}
193
+ closeWorkspaceWithSavedChanges={jest.fn()}
194
+ promptBeforeClosing={jest.fn()}
195
+ />,
196
+ );
166
197
 
167
198
  await user.click(screen.getByText('Cancel'));
168
-
169
- expect(closeOverlay).toHaveBeenCalled();
170
199
  });
171
200
  });
@@ -1,20 +1,26 @@
1
- import { ModalHeader, ModalBody, ModalFooter, Button, Form, Select, TextInput, SelectItem } from '@carbon/react';
1
+ import { Button, Form, Select, TextInput, SelectItem, ButtonSet } from '@carbon/react';
2
2
  import React, { type ChangeEvent, useCallback, useState } from 'react';
3
3
  import styles from './add-stock-sources.scss';
4
4
  import { useConcept } from '../../stock-lookups/stock-lookups.resource';
5
5
  import { type StockSource } from '../../core/api/types/stockOperation/StockSource';
6
6
  import { createOrUpdateStockSource } from '../stock-sources.resource';
7
- import { restBaseUrl, showSnackbar, useConfig } from '@openmrs/esm-framework';
7
+ import {
8
+ type DefaultWorkspaceProps,
9
+ restBaseUrl,
10
+ showSnackbar,
11
+ useConfig,
12
+ getCoreTranslation,
13
+ } from '@openmrs/esm-framework';
8
14
  import { useTranslation } from 'react-i18next';
9
- import { closeOverlay } from '../../core/components/overlay/hook';
10
15
  import { type ConfigObject } from '../../config-schema';
11
16
  import { handleMutate } from '../../utils';
17
+ import { Save } from '@carbon/react/icons';
12
18
 
13
- interface AddStockSourceProps {
19
+ type AddStockSourceProps = DefaultWorkspaceProps & {
14
20
  model?: StockSource;
15
- }
21
+ };
16
22
 
17
- const StockSourcesAddOrUpdate: React.FC<AddStockSourceProps> = ({ model }) => {
23
+ const StockSourcesAddOrUpdate: React.FC<AddStockSourceProps> = ({ model, closeWorkspace }) => {
18
24
  const { t } = useTranslation();
19
25
  const { stockSourceTypeUUID } = useConfig<ConfigObject>();
20
26
 
@@ -38,7 +44,7 @@ const StockSourcesAddOrUpdate: React.FC<AddStockSourceProps> = ({ model }) => {
38
44
  setFormModel({ ...formModel, sourceType: selectedSourceType });
39
45
  };
40
46
 
41
- const onFormSubmit = useCallback(
47
+ const handleSave = useCallback(
42
48
  (event) => {
43
49
  event.preventDefault();
44
50
  if (model) {
@@ -57,7 +63,7 @@ const StockSourcesAddOrUpdate: React.FC<AddStockSourceProps> = ({ model }) => {
57
63
 
58
64
  handleMutate(`${restBaseUrl}/stockmanagement/stocksource`);
59
65
 
60
- closeOverlay();
66
+ closeWorkspace();
61
67
  },
62
68
  (error) => {
63
69
  showSnackbar({
@@ -66,62 +72,63 @@ const StockSourcesAddOrUpdate: React.FC<AddStockSourceProps> = ({ model }) => {
66
72
  isLowContrast: true,
67
73
  subtitle: error?.message,
68
74
  });
75
+ closeWorkspace();
69
76
  },
70
77
  )
71
78
  .catch();
72
79
  },
73
- [formModel, model, t],
80
+ [formModel, model, t, closeWorkspace],
74
81
  );
75
82
  return (
76
- <div>
77
- <Form onSubmit={onFormSubmit}>
78
- <ModalHeader />
79
- <ModalBody>
80
- <section className={styles.section}>
81
- <TextInput
82
- id="fullname"
83
- type="text"
84
- labelText={t('fullName', 'Full Name')}
85
- size="md"
86
- onChange={onNameChanged}
87
- value={model?.name}
88
- placeholder="e.g National Medical Stores"
89
- />
90
- </section>
91
- <section className={styles.section}>
92
- <TextInput
93
- id="acronym"
94
- type="text"
95
- size="md"
96
- placeholder="e.g NMS"
97
- onChange={onAcronymChanged}
98
- value={model?.acronym}
99
- labelText={t('acronym', 'Acronym/Code')}
100
- />
101
- </section>
102
- <section className={styles.section}>
103
- <Select
104
- name="sourceType"
105
- className="select-field"
106
- labelText={t('sourceType', 'Source Type')}
107
- id="sourceType"
108
- value={formModel?.sourceType ? formModel.sourceType.uuid : ''}
109
- onChange={onSourceTypeChange}
110
- >
111
- <SelectItem disabled hidden value="" text={t('chooseSourceType', 'Choose a source type')} />
112
- {items?.answers?.map((sourceType) => {
113
- return <SelectItem key={sourceType.uuid} value={sourceType.uuid} text={sourceType.display} />;
114
- })}
115
- </Select>
116
- </section>
117
- </ModalBody>
118
- <ModalFooter>
119
- <Button kind="secondary" onClick={closeOverlay}>
120
- {t('cancel', 'Cancel')}
121
- </Button>
122
- <Button type="submit">{t('save', 'Save')}</Button>
123
- </ModalFooter>
124
- </Form>
83
+ <div className={styles.formContainer}>
84
+ <div className={styles.body}>
85
+ <section className={styles.section}>
86
+ <TextInput
87
+ id="fullname"
88
+ type="text"
89
+ labelText={t('fullName', 'Full Name')}
90
+ size="md"
91
+ onChange={onNameChanged}
92
+ value={model?.name}
93
+ placeholder="e.g National Medical Stores"
94
+ />
95
+ </section>
96
+ <section className={styles.section}>
97
+ <TextInput
98
+ id="acronym"
99
+ type="text"
100
+ size="md"
101
+ placeholder="e.g NMS"
102
+ onChange={onAcronymChanged}
103
+ value={model?.acronym}
104
+ labelText={t('acronym', 'Acronym/Code')}
105
+ />
106
+ </section>
107
+ <section className={styles.section}>
108
+ <Select
109
+ name="sourceType"
110
+ className="select-field"
111
+ labelText={t('sourceType', 'Source Type')}
112
+ id="sourceType"
113
+ value={formModel?.sourceType ? formModel.sourceType.uuid : ''}
114
+ onChange={onSourceTypeChange}
115
+ >
116
+ <SelectItem disabled hidden value="" text={t('chooseSourceType', 'Choose a source type')} />
117
+ {items?.answers?.map((sourceType) => (
118
+ <SelectItem key={sourceType.uuid} value={sourceType.uuid} text={sourceType.display} />
119
+ ))}
120
+ </Select>
121
+ </section>
122
+ </div>
123
+
124
+ <ButtonSet className={styles.buttonSet}>
125
+ <Button kind="secondary" onClick={closeWorkspace} className={styles.button}>
126
+ {getCoreTranslation('cancel', 'Cancel')}
127
+ </Button>
128
+ <Button type="submit" className={styles.button} onClick={handleSave} kind="primary" renderIcon={Save}>
129
+ {getCoreTranslation('save', 'Save')}
130
+ </Button>
131
+ </ButtonSet>
125
132
  </div>
126
133
  );
127
134
  };
@@ -3,9 +3,8 @@ import { Button } from '@carbon/react';
3
3
  import { Edit } from '@carbon/react/icons';
4
4
 
5
5
  import { useTranslation } from 'react-i18next';
6
- import { launchOverlay } from '../../core/components/overlay/hook';
7
- import StockSourcesAddOrUpdate from '../add-stock-sources/add-stock-sources.component';
8
6
  import { type StockSource } from '../../core/api/types/stockOperation/StockSource';
7
+ import { launchWorkspace } from '@openmrs/esm-framework';
9
8
 
10
9
  interface EditStockSourcesActionMenuProps {
11
10
  data?: StockSource;
@@ -14,15 +13,18 @@ interface EditStockSourcesActionMenuProps {
14
13
  const EditStockSourceActionsMenu: React.FC<EditStockSourcesActionMenuProps> = ({ data }) => {
15
14
  const { t } = useTranslation();
16
15
  const handleClick = useCallback(() => {
17
- launchOverlay('Edit Stock Source', <StockSourcesAddOrUpdate model={data} />);
18
- }, [data]);
16
+ launchWorkspace('stock-sources-form-workspace', {
17
+ workspaceTitle: t('editStockSource', 'Edit stock source'),
18
+ model: data,
19
+ });
20
+ }, [data, t]);
19
21
 
20
22
  return (
21
23
  <Button
22
24
  kind="ghost"
23
25
  size="md"
24
26
  onClick={() => handleClick()}
25
- iconDescription={t('editStockItem', 'Edit Stock Item')}
27
+ iconDescription={t('editStockSource', 'Edit stock source')}
26
28
  renderIcon={(props) => <Edit size={16} {...props} />}
27
29
  />
28
30
  );
@@ -1,19 +1,20 @@
1
1
  import { Button } from '@carbon/react';
2
2
  import React, { useCallback } from 'react';
3
3
  import { useTranslation } from 'react-i18next';
4
- import { launchOverlay } from '../core/components/overlay/hook';
5
- import AddStockUserRoleScope from './add-stock-user-scope/add-stock-user-role-scope.component';
4
+ import { launchWorkspace } from '@openmrs/esm-framework';
6
5
 
7
6
  const AddStockUserRoleScopeActionButton: React.FC = () => {
8
7
  const { t } = useTranslation();
9
8
 
10
9
  const handleClick = useCallback(() => {
11
- launchOverlay(t('addStockUserRoleScope', 'Add Stock User Role Scope'), <AddStockUserRoleScope />);
10
+ launchWorkspace('stock-user-role-scopes-form-workspace', {
11
+ workspaceTitle: t('addNewUserRoleScope', 'Add new user role scope'),
12
+ });
12
13
  }, [t]);
13
14
 
14
15
  return (
15
16
  <Button onClick={handleClick} size="md" kind="primary">
16
- {t('addNewUserRoleScope', 'Add New User Role Scope')}
17
+ {t('addNewUserRoleScope', 'Add new user role scope')}
17
18
  </Button>
18
19
  );
19
20
  };
@@ -38,3 +38,24 @@
38
38
  .checkbox {
39
39
  width: 100%;
40
40
  }
41
+
42
+ .formContainer {
43
+ display: flex;
44
+ flex-direction: column;
45
+ justify-content: space-between;
46
+ width: 100%;
47
+ height: 100%;
48
+ }
49
+
50
+ .button {
51
+ display: flex;
52
+ align-content: flex-start;
53
+ align-items: baseline;
54
+ min-width: 50%;
55
+ }
56
+
57
+ .buttonSet {
58
+ display: flex;
59
+ justify-content: space-between;
60
+ width: 100%;
61
+ }