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

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.
@@ -1,19 +1,31 @@
1
+ import React, { type ChangeEvent, useEffect, useState } from 'react';
2
+ import classNames from 'classnames';
1
3
  import {
2
4
  Button,
5
+ ButtonSet,
3
6
  Checkbox,
4
7
  CheckboxGroup,
8
+ ComboBox,
9
+ DatePicker,
10
+ DatePickerInput,
5
11
  Form,
12
+ FormGroup,
6
13
  InlineLoading,
7
- Toggle,
8
- DatePickerInput,
9
- DatePicker,
10
- ComboBox,
11
14
  Select,
12
15
  SelectItem,
13
- ButtonSet,
16
+ Stack,
17
+ Toggle,
14
18
  } from '@carbon/react';
15
- import React, { type ChangeEvent, useEffect, useState } from 'react';
16
- import styles from './add-stock-user-role-scope.scss';
19
+ import { Save } from '@carbon/react/icons';
20
+ import { useTranslation } from 'react-i18next';
21
+ import {
22
+ type DefaultWorkspaceProps,
23
+ getCoreTranslation,
24
+ restBaseUrl,
25
+ showSnackbar,
26
+ useLayoutType,
27
+ useSession,
28
+ } from '@openmrs/esm-framework';
17
29
  import {
18
30
  useRoles,
19
31
  useStockOperationTypes,
@@ -22,34 +34,26 @@ import {
22
34
  useUsers,
23
35
  } from '../../stock-lookups/stock-lookups.resource';
24
36
  import { ResourceRepresentation } from '../../core/api/api';
25
- import { useTranslation } from 'react-i18next';
26
37
  import { type UserRoleScope } from '../../core/api/types/identity/UserRoleScope';
27
38
  import { createOrUpdateUserRoleScope } from '../stock-user-role-scopes.resource';
28
- import {
29
- type DefaultWorkspaceProps,
30
- restBaseUrl,
31
- showSnackbar,
32
- useSession,
33
- getCoreTranslation,
34
- } from '@openmrs/esm-framework';
35
39
  import { type UserRoleScopeOperationType } from '../../core/api/types/identity/UserRoleScopeOperationType';
36
40
  import { type UserRoleScopeLocation } from '../../core/api/types/identity/UserRoleScopeLocation';
37
41
  import {
38
42
  DATE_PICKER_CONTROL_FORMAT,
39
43
  DATE_PICKER_FORMAT,
44
+ formatForDatePicker,
40
45
  INVENTORY_ADMNISTRATOR_ROLE_UUID,
41
46
  INVENTORY_CLERK_ROLE_UUID,
42
47
  INVENTORY_DISPENSING_ROLE_UUID,
43
48
  INVENTORY_MANAGER_ROLE_UUID,
44
49
  INVENTORY_REPORTING_ROLE_UUID,
45
- formatForDatePicker,
46
50
  today,
47
51
  } from '../../constants';
48
- import { type User } from '../../core/api/types/identity/User';
49
52
  import { type Role } from '../../core/api/types/identity/Role';
50
53
  import { type StockOperationType } from '../../core/api/types/stockOperation/StockOperationType';
54
+ import { type User } from '../../core/api/types/identity/User';
51
55
  import { handleMutate } from '../../utils';
52
- import { Save } from '@carbon/react/icons';
56
+ import styles from './add-stock-user-role-scope.scss';
53
57
 
54
58
  const MinDate: Date = today();
55
59
 
@@ -62,10 +66,13 @@ const AddStockUserRoleScope: React.FC<AddStockUserRoleScopeProps> = ({ model, ed
62
66
  const { t } = useTranslation();
63
67
  const currentUser = useSession();
64
68
  const [formModel, setFormModel] = useState<UserRoleScope>({ ...model });
69
+ const isTablet = useLayoutType() === 'tablet';
65
70
 
66
71
  const [roles, setRoles] = useState<Role[]>([]);
67
72
 
68
73
  const loggedInUserUuid = currentUser?.user?.uuid;
74
+ const [selectedUserUuid, setSelectedUserUuid] = useState<string | null>(null);
75
+ const { data: user } = useUser(selectedUserUuid);
69
76
 
70
77
  // operation types
71
78
  const {
@@ -78,9 +85,6 @@ const AddStockUserRoleScope: React.FC<AddStockUserRoleScopeProps> = ({ model, ed
78
85
  v: ResourceRepresentation.Default,
79
86
  });
80
87
 
81
- const [selectedUserUuid, setSelectedUserUuid] = useState<string | null>(null);
82
- const { data: user } = useUser(selectedUserUuid);
83
-
84
88
  // get roles
85
89
  const { isLoading: loadingRoles } = useRoles({
86
90
  v: ResourceRepresentation.Default,
@@ -244,186 +248,160 @@ const AddStockUserRoleScope: React.FC<AddStockUserRoleScopeProps> = ({ model, ed
244
248
  );
245
249
  }
246
250
  return (
247
- <div className={styles.formContainer}>
248
- <div style={{ padding: '1rem' }}>
249
- <section className={styles.section}>
250
- <div>
251
- {users?.results?.length > 0 && (
252
- <>
253
- <span className={styles.subTitle}>{t('user', 'User')}</span>
251
+ <Form className={styles.container}>
252
+ <Stack className={styles.form} gap={5}>
253
+ <div>
254
+ {users?.results?.length > 0 && (
255
+ <>
256
+ <FormGroup legendText={t('user', 'User')}>
254
257
  <ComboBox
255
258
  id="userName"
256
- size="md"
257
- labelText={t('user', 'User')}
259
+ initialSelectedItem={usersResults.find((user) => user.uuid === model?.userUuid) ?? null}
258
260
  items={filteredItems.length ? filteredItems : usersResults}
259
- onChange={onUserChanged}
260
- shouldFilterItem={() => true}
261
261
  itemToString={(item) => `${item?.person?.display ?? item?.display ?? ''}`}
262
+ labelText={t('user', 'User')}
263
+ onChange={onUserChanged}
262
264
  onInputChange={handleSearchQueryChange}
263
265
  placeholder="Filter..."
264
- initialSelectedItem={usersResults.find((user) => user.uuid === model?.userUuid) ?? null}
266
+ shouldFilterItem={() => true}
267
+ size="md"
265
268
  />
266
- </>
267
- )}
268
- </div>
269
- </section>
270
- <section className={styles.section}>
271
- <div>
272
- <Select
273
- name="role"
274
- className="select-field"
275
- labelText={t('role', 'Role')}
276
- id="select-role"
277
- value={formModel.role ?? 'placeholder-item'}
278
- onChange={onRoleChange}
279
- >
280
- <SelectItem disabled hidden value="placeholder-item" text={t('chooseARole', 'Choose a role')} />
281
-
282
- {editMode ? (
283
- <SelectItem key={formModel?.role} value={formModel?.role} text={formModel?.role} />
284
- ) : (
285
- (user?.roles ?? roles)?.map((role) => {
286
- return <SelectItem key={role.display} value={role.display} text={role.display} />;
287
- })
288
- )}
289
- </Select>
290
- </div>
291
- </section>
292
- <section className={styles.section}>
293
- <CheckboxGroup className={styles.checkboxGrid}>
294
- <Checkbox
295
- onChange={onEnabledChanged}
296
- checked={formModel?.enabled}
297
- labelText={t('enabled', 'Enabled ?')}
298
- value={model?.enabled}
299
- id="chk-userEnabled"
300
- />
301
- <Checkbox
302
- onChange={onPermanentChanged}
303
- name="isPermanent"
304
- checked={formModel?.permanent}
305
- value={model?.permanent}
306
- labelText={t('permanent', 'Permanent ?')}
307
- id="chk-userPermanent"
308
- />
309
-
310
- {!formModel?.permanent && (
311
- <>
312
- <DatePicker
313
- datePickerType="range"
314
- light
315
- minDate={formatForDatePicker(MinDate)}
316
- locale="en"
317
- dateFormat={DATE_PICKER_CONTROL_FORMAT}
318
- onChange={onActiveDatesChange}
319
- >
320
- <DatePickerInput
321
- id="date-picker-input-id-start"
322
- name="activeFrom"
323
- placeholder={DATE_PICKER_FORMAT}
324
- labelText={t('activeFrom', 'Active From')}
325
- value={formatForDatePicker(formModel?.activeFrom)}
269
+ </FormGroup>
270
+ </>
271
+ )}
272
+ </div>
273
+ <Select
274
+ id="select-role"
275
+ labelText={t('role', 'Role')}
276
+ name="role"
277
+ onChange={onRoleChange}
278
+ value={formModel.role}
279
+ >
280
+ <SelectItem value={''} text={t('chooseARole', 'Choose a role')} />
281
+ {editMode ? (
282
+ <SelectItem key={formModel?.role} value={formModel?.role} text={formModel?.role} />
283
+ ) : (
284
+ (user?.roles ?? roles)?.map((role) => {
285
+ return <SelectItem key={role.display} value={role.display} text={role.display} />;
286
+ })
287
+ )}
288
+ </Select>
289
+ <CheckboxGroup className={styles.checkboxGrid}>
290
+ <Checkbox
291
+ checked={formModel?.enabled}
292
+ id="chk-userEnabled"
293
+ labelText={t('enabled', 'Enabled')}
294
+ onChange={onEnabledChanged}
295
+ value={model?.enabled}
296
+ />
297
+ <Checkbox
298
+ checked={formModel?.permanent}
299
+ id="chk-userPermanent"
300
+ labelText={t('permanent', 'Permanent')}
301
+ name="isPermanent"
302
+ onChange={onPermanentChanged}
303
+ value={model?.permanent}
304
+ />
305
+
306
+ {!formModel?.permanent && (
307
+ <>
308
+ <DatePicker
309
+ dateFormat={DATE_PICKER_CONTROL_FORMAT}
310
+ datePickerType="range"
311
+ light
312
+ locale="en"
313
+ minDate={formatForDatePicker(MinDate)}
314
+ onChange={onActiveDatesChange}
315
+ >
316
+ <DatePickerInput
317
+ id="date-picker-input-id-start"
318
+ labelText={t('activeFrom', 'Active From')}
319
+ name="activeFrom"
320
+ placeholder={DATE_PICKER_FORMAT}
321
+ value={formatForDatePicker(formModel?.activeFrom)}
322
+ />
323
+ <DatePickerInput
324
+ id="date-picker-input-id-finish"
325
+ labelText={t('activeTo', 'Active To')}
326
+ name="activeTo"
327
+ placeholder={DATE_PICKER_FORMAT}
328
+ value={formatForDatePicker(formModel?.activeTo)}
329
+ />
330
+ </DatePicker>
331
+ </>
332
+ )}
333
+ </CheckboxGroup>
334
+ <FormGroup legendText={t('stockOperation', 'Stock Operations')}>
335
+ <span className={styles.subTitle}>
336
+ {t('roleDescription', 'The role will be applicable to only selected stock operations.')}
337
+ </span>
338
+ </FormGroup>
339
+ <CheckboxGroup className={styles.checkboxGrid}>
340
+ {stockOperations?.length > 0 &&
341
+ stockOperations.map((type) => {
342
+ return (
343
+ <div className={styles.flexRow}>
344
+ <Checkbox
345
+ checked={isOperationChecked(type)}
346
+ className={styles.checkbox}
347
+ id={type.uuid}
348
+ labelText={type.name}
349
+ onChange={(event) => onStockOperationTypeChanged(event)}
350
+ value={type.uuid}
326
351
  />
327
- <DatePickerInput
328
- id="date-picker-input-id-finish"
329
- name="activeTo"
330
- placeholder={DATE_PICKER_FORMAT}
331
- labelText={t('activeTo', 'Active To')}
332
- value={formatForDatePicker(formModel?.activeTo)}
352
+ </div>
353
+ );
354
+ })}
355
+ </CheckboxGroup>
356
+ <FormGroup legendText={t('locations', 'Locations')}>
357
+ <span className={styles.subTitle}>
358
+ {t('toggleMessage', 'Use the toggle to apply this scope to the locations under the selected location.')}
359
+ </span>
360
+ </FormGroup>
361
+ <CheckboxGroup className={styles.checkboxGrid}>
362
+ {stockLocations?.length > 0 &&
363
+ stockLocations.map((type) => {
364
+ const checkedLocation = findCheckedLocation(type);
365
+
366
+ const getToggledValue = (locationUuid) => {
367
+ const location = checkedLocation?.locationUuid === locationUuid ? checkedLocation : null;
368
+ return location?.enableDescendants === true;
369
+ };
370
+
371
+ return (
372
+ <div className={styles.flexRow}>
373
+ <Checkbox
374
+ checked={checkedLocation != null}
375
+ className={styles.checkbox}
376
+ id={`chk-loc-child-${type.id}`}
377
+ key={`chk-loc-child-key-${type.id}`}
378
+ labelText={type.name}
379
+ name="location"
380
+ onChange={(event) => onLocationCheckBoxChanged(event)}
381
+ value={type.id}
333
382
  />
334
- </DatePicker>
335
- </>
336
- )}
337
- </CheckboxGroup>
338
- </section>
339
- <br />
340
- <section className={styles.section}>
341
- <div style={{ display: 'flex', flexDirection: 'column' }}>
342
- <span className={styles.sectionTitle}> {t('stockOperation', 'Stock Operations')}</span>
343
- <div className={styles.hr} />
344
- <span className={styles.subTitle}>
345
- {t('roleDescription', 'The role will be applicable to only selected stock operations.')}
346
- </span>
347
- </div>
348
- </section>
349
- <section className={styles.section}>
350
- <CheckboxGroup className={styles.checkboxGrid}>
351
- {stockOperations?.length > 0 &&
352
- stockOperations.map((type) => {
353
- return (
354
- <div style={{ display: 'flex', flexDirection: 'row' }}>
355
- <Checkbox
356
- value={type.uuid}
357
- checked={isOperationChecked(type)}
358
- className={styles.checkbox}
359
- onChange={(event) => onStockOperationTypeChanged(event)}
360
- labelText={type.name}
361
- id={type.uuid}
362
- />
363
- </div>
364
- );
365
- })}
366
- </CheckboxGroup>
367
- </section>
368
- <br />
369
- <section className={styles.section}>
370
- <div style={{ display: 'flex', flexDirection: 'column' }}>
371
- <span className={styles.sectionTitle}> {t('locations', 'Locations')}</span>
372
- <div className={styles.hr} />
373
- <span className={styles.subTitle}>
374
- {t('toggleMessage', 'Use the toggle to apply this scope to the locations under the selected location.')}
375
- </span>
376
- </div>
377
- </section>
378
- <section className={styles.section}>
379
- <CheckboxGroup className={styles.checkboxGrid}>
380
- {stockLocations?.length > 0 &&
381
- stockLocations.map((type) => {
382
- const checkedLocation = findCheckedLocation(type);
383
-
384
- const getToggledValue = (locationUuid) => {
385
- const location = checkedLocation?.locationUuid === locationUuid ? checkedLocation : null;
386
- return location?.enableDescendants === true;
387
- };
388
-
389
- return (
390
- <div
391
- style={{
392
- display: 'flex',
393
- flexDirection: 'row',
394
- margin: '4px',
395
- padding: '5px',
396
- }}
397
- >
398
- <Checkbox
399
- name="location"
400
- key={`chk-loc-child-key-${type.id}`}
401
- id={`chk-loc-child-${type.id}`}
383
+ {checkedLocation && (
384
+ <Toggle
385
+ className={styles.toggle}
386
+ hideLabel
387
+ id={`tg-loc-child-${type.id}`}
388
+ key={`tg-loc-child-key-${type.id}`}
389
+ onToggleClick={getToggledValue(type.id)}
390
+ size="sm"
402
391
  value={type.id}
403
- onChange={(event) => onLocationCheckBoxChanged(event)}
404
- className={styles.checkbox}
405
- labelText={type.name}
406
- checked={checkedLocation != null}
407
392
  />
408
- {checkedLocation && (
409
- <Toggle
410
- value={type.id}
411
- hideLabel
412
- className={styles.toggle}
413
- size={'sm'}
414
- onToggleClick={getToggledValue(type.id)}
415
- key={`tg-loc-child-key-${type.id}`}
416
- id={`tg-loc-child-${type.id}`}
417
- />
418
- )}
419
- </div>
420
- );
421
- })}
422
- </CheckboxGroup>
423
- </section>
424
- </div>
425
-
426
- <ButtonSet className={styles.buttonSet}>
393
+ )}
394
+ </div>
395
+ );
396
+ })}
397
+ </CheckboxGroup>
398
+ </Stack>
399
+ <ButtonSet
400
+ className={classNames(styles.buttonSet, {
401
+ [styles.tablet]: isTablet,
402
+ [styles.desktop]: !isTablet,
403
+ })}
404
+ >
427
405
  <Button kind="secondary" onClick={closeWorkspace} className={styles.button}>
428
406
  {getCoreTranslation('cancel', 'Cancel')}
429
407
  </Button>
@@ -431,7 +409,7 @@ const AddStockUserRoleScope: React.FC<AddStockUserRoleScopeProps> = ({ model, ed
431
409
  {getCoreTranslation('save', 'Save')}
432
410
  </Button>
433
411
  </ButtonSet>
434
- </div>
412
+ </Form>
435
413
  );
436
414
  };
437
415
 
@@ -4,7 +4,6 @@ import {
4
4
  DataTable,
5
5
  DataTableSkeleton,
6
6
  Link,
7
- TabPanel,
8
7
  Pagination,
9
8
  Table,
10
9
  TableBody,
@@ -14,28 +13,29 @@ import {
14
13
  TableHeader,
15
14
  TableRow,
16
15
  TableToolbar,
16
+ TableToolbarAction,
17
17
  TableToolbarContent,
18
+ TableToolbarMenu,
18
19
  TableToolbarSearch,
20
+ TabPanel,
19
21
  Tile,
20
- TableToolbarMenu,
21
- TableToolbarAction,
22
22
  } from '@carbon/react';
23
- import styles from './stock-user-role-scopes.scss';
24
23
  import { ArrowDownLeft, ArrowLeft } from '@carbon/react/icons';
25
24
  import { isDesktop, restBaseUrl, useSession } from '@openmrs/esm-framework';
25
+ import { formatDisplayDate } from '../core/utils/datetimeUtils';
26
+ import { handleMutate } from '../utils';
26
27
  import { ResourceRepresentation } from '../core/api/api';
27
- import useStockUserRoleScopesPage from './stock-user-role-scopes-items-table.resource';
28
+ import { URL_USER_ROLE_SCOPE } from '../constants';
28
29
  import AddStockUserRoleScopeActionButton from './add-stock-user-role-scope-button.component';
29
- import { formatDisplayDate } from '../core/utils/datetimeUtils';
30
30
  import EditStockUserRoleActionsMenu from './edit-stock-user-scope/edit-stock-user-scope-action-menu.component';
31
31
  import StockUserScopeDeleteActionMenu from './delete-stock-user-scope/delete-stock-user-scope.component';
32
- import { URL_USER_ROLE_SCOPE } from '../constants';
33
- import { handleMutate } from '../utils';
32
+ import useStockUserRoleScopesPage from './stock-user-role-scopes-items-table.resource';
33
+ import styles from './stock-user-role-scopes.scss';
34
34
 
35
35
  function StockUserRoleScopesItems() {
36
36
  const { t } = useTranslation();
37
-
38
37
  const currentUser = useSession();
38
+
39
39
  const handleRefresh = () => {
40
40
  handleMutate(`${restBaseUrl}/stockmanagement/userrolescope`);
41
41
  };
@@ -46,6 +46,7 @@ function StockUserRoleScopesItems() {
46
46
  v: ResourceRepresentation.Default,
47
47
  totalCount: true,
48
48
  });
49
+
49
50
  const tableHeaders = useMemo(
50
51
  () => [
51
52
  {
@@ -71,7 +72,7 @@ function StockUserRoleScopesItems() {
71
72
  },
72
73
  {
73
74
  id: 4,
74
- header: t('permanent', 'Permanent ?'),
75
+ header: t('permanent', 'Permanent'),
75
76
  key: 'permanent',
76
77
  },
77
78
  {
@@ -164,15 +165,11 @@ function StockUserRoleScopesItems() {
164
165
  'To access stock management features, users must have assigned roles specifying location and stock operation type scopes.',
165
166
  )}
166
167
  </TabPanel>
167
- <div id="table-tool-bar">
168
- <div></div>
169
- <div className="right-filters"></div>
170
- </div>
171
168
  <DataTable
172
- rows={tableRows ?? []}
173
169
  headers={tableHeaders}
174
- isSortable={true}
175
- useZebraStyles={true}
170
+ isSortable
171
+ rows={tableRows ?? []}
172
+ useZebraStyles
176
173
  render={({ rows, headers, getHeaderProps, getTableProps, getRowProps, onInputChange }) => (
177
174
  <TableContainer>
178
175
  <TableToolbar
@@ -185,7 +182,9 @@ function StockUserRoleScopesItems() {
185
182
  <TableToolbarContent className={styles.toolbarContent}>
186
183
  <TableToolbarSearch persistent onChange={onInputChange} />
187
184
  <TableToolbarMenu>
188
- <TableToolbarAction onClick={handleRefresh}>Refresh</TableToolbarAction>
185
+ <TableToolbarAction className={styles.toolbarAction} onClick={handleRefresh}>
186
+ {t('refresh', 'Refresh')}
187
+ </TableToolbarAction>
189
188
  </TableToolbarMenu>
190
189
  <AddStockUserRoleScopeActionButton />
191
190
  </TableToolbarContent>
@@ -230,7 +229,9 @@ function StockUserRoleScopesItems() {
230
229
  <div className={styles.tileContainer}>
231
230
  <Tile className={styles.tile}>
232
231
  <div className={styles.tileContent}>
233
- <p className={styles.content}>{t('noUserScopesToDisplay', 'No Stock User scopes to display')}</p>
232
+ <p className={styles.content}>
233
+ {t('noStockUserRoleScopesToDisplay', 'No stock user role scopes to display')}
234
+ </p>
234
235
  <p className={styles.helper}>{t('checkFilters', 'Check the filters above')}</p>
235
236
  </div>
236
237
  </Tile>
@@ -60,3 +60,7 @@
60
60
  flex-direction: column;
61
61
  align-items: center;
62
62
  }
63
+
64
+ .toolbarAction {
65
+ max-width: none;
66
+ }
@@ -101,7 +101,7 @@
101
101
  "editStockOperation": "Edit stock operation",
102
102
  "editStockRule": "Edit Stock Rule",
103
103
  "editUserScope": "Edit UserScope",
104
- "enabled": "Enabled ?",
104
+ "enabled": "Enabled",
105
105
  "endDate": "End Date",
106
106
  "ended": "Ended",
107
107
  "enterRemarks": "Enter remarks",
@@ -217,7 +217,7 @@
217
217
  "partialFulfillment": "Partial Fulfillment",
218
218
  "partieserror": "Error launching base operation details form",
219
219
  "patients": "Patients",
220
- "permanent": "Permanent ?",
220
+ "permanent": "Permanent",
221
221
  "pleaseFillField": "",
222
222
  "pleaseSpecify": "Please Specify",
223
223
  "poorquality": "Poor Quality",