Skip to content

Commit

Permalink
UIIN-3116: Add call number browse settings. (#2694)
Browse files Browse the repository at this point in the history
* UIIN-3116: Add call number browse settings.

* add proptypes for components

* UIIN-3116 added a call bumber browse settings permission to translations

---------

Co-authored-by: Denys Bohdan <[email protected]>
  • Loading branch information
Dmytro-Melnyshyn and BogdanDenis authored Dec 17, 2024
1 parent b1d9300 commit e2d8705
Show file tree
Hide file tree
Showing 14 changed files with 493 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
* React v19: refactor away from default props for functional components. Refs UIIN-2890.
* User can edit Source consortium "Holdings sources" in member tenant but not in Consortia manager. Refs UIIN-3147.
* React 19: refactor away from react-dom/test-utils. Refs UIIN-2888.
* Add call number browse settings. Refs UIIN-3116.

## [12.0.7] (IN PROGRESS)

Expand Down
12 changes: 12 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,18 @@
],
"visible": true
},
{
"permissionName": "ui-inventory.settings.call-number-browse",
"displayName": "Settings (Inventory): Configure call number browse",
"subPermissions": [
"settings.inventory.enabled",
"inventory-storage.call-number-types.collection.get",
"browse.config.collection.get",
"browse.config.item.put",
"perms.users.get"
],
"visible": true
},
{
"permissionName": "ui-inventory.settings.classification-browse",
"displayName": "Settings (Inventory): Configure classification browse",
Expand Down
1 change: 1 addition & 0 deletions src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ export { default as useUpdateOwnership } from './useUpdateOwnership';
export { default as useLocalStorageItems } from './useLocalStorageItems';
export * from './useQuickExport';
export * from '@folio/stripes-inventory-components/lib/queries/useInstanceDateTypes';
export * from './useCallNumberTypesQuery';
1 change: 1 addition & 0 deletions src/hooks/useCallNumberTypesQuery/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './useCallNumberTypesQuery';
22 changes: 22 additions & 0 deletions src/hooks/useCallNumberTypesQuery/useCallNumberTypesQuery.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useQuery } from 'react-query';

import {
useNamespace,
useOkapiKy,
} from '@folio/stripes/core';
import { CQL_FIND_ALL } from '@folio/stripes-inventory-components';

export const useCallNumberTypesQuery = ({ tenantId } = {}) => {
const ky = useOkapiKy({ tenant: tenantId });
const [namespace] = useNamespace({ key: 'call-number-types' });

const { data, isFetching } = useQuery(
[namespace, tenantId],
() => ky.get(`call-number-types?limit=2000&query=${CQL_FIND_ALL} sortby name`).json(),
);

return {
callNumberTypes: data?.callNumberTypes || [],
isCallNumberTypesLoading: isFetching,
};
};
44 changes: 44 additions & 0 deletions src/hooks/useCallNumberTypesQuery/useCallNumberTypesQuery.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
QueryClient,
QueryClientProvider,
} from 'react-query';

import {
renderHook,
act,
} from '@folio/jest-config-stripes/testing-library/react';
import { useOkapiKy } from '@folio/stripes/core';

import { useCallNumberTypesQuery } from './useCallNumberTypesQuery';

const queryClient = new QueryClient();

const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);

describe('useCallNumberTypesQuery', () => {
beforeEach(() => {
useOkapiKy.mockClear().mockReturnValue({
get: () => ({
json: () => Promise.resolve({
callNumberTypes: [{ id: 'call-number-type-id' }],
}),
}),
});
});

afterEach(() => {
jest.clearAllMocks();
});

it('should fetch call number types', async () => {
const { result } = renderHook(() => useCallNumberTypesQuery(), { wrapper });

await act(() => !result.current.isLoading);

expect(result.current.callNumberTypes).toEqual([{ id: 'call-number-type-id' }]);
});
});
142 changes: 142 additions & 0 deletions src/settings/CallNumberBrowseSettings/CallNumberBrowseSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import {
useCallback,
useMemo,
} from 'react';
import {
FormattedMessage,
useIntl,
} from 'react-intl';

import { ControlledVocab } from '@folio/stripes/smart-components';
import {
InfoPopover,
LoadingPane,
} from '@folio/stripes/components';
import {
TitleManager,
useStripes,
useUserTenantPermissions,
} from '@folio/stripes/core';

import { CallNumberTypeList } from './CallNumberTypeList';
import { CallNumberTypeField } from './CallNumberTypeField';
import { useCallNumberTypesQuery } from '../../hooks';
import { CALL_NUMBER_BROWSE_COLUMNS } from './constants';

const CallNumberBrowseSettings = () => {
const stripes = useStripes();
const intl = useIntl();
const centralTenantId = stripes.user.user?.consortium?.centralTenantId;
const { callNumberTypes, isCallNumberTypesLoading } = useCallNumberTypesQuery({ tenantId: centralTenantId });
const ConnectedControlledVocab = useMemo(() => stripes.connect(ControlledVocab), [stripes]);

const {
userPermissions: centralTenantPermissions,
isFetching: isCentralTenantPermissionsLoading,
} = useUserTenantPermissions({
tenantId: centralTenantId,
});

const permission = 'ui-inventory.settings.call-number-browse';
const hasCentralTenantPerm = centralTenantPermissions.some(({ permissionName }) => permissionName === permission);
const hasRequiredPermissions = stripes.hasInterface('consortia')
? (hasCentralTenantPerm && stripes.hasPerm(permission))
: stripes.hasPerm(permission);

const fieldLabels = {
[CALL_NUMBER_BROWSE_COLUMNS.ID]: intl.formatMessage({ id: 'ui-inventory.name' }),
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: intl.formatMessage({ id: 'ui-inventory.callNumberTypes' }),
};

const columnMapping = useMemo(() => ({
[CALL_NUMBER_BROWSE_COLUMNS.ID]: fieldLabels[CALL_NUMBER_BROWSE_COLUMNS.ID],
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: (
<>
{fieldLabels[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]}
<InfoPopover
content={intl.formatMessage({ id: 'ui-inventory.settings.instanceCallNumber.identifierTypesPopover' })}
/>
</>
),
}));

const callNumberTypeOptions = useMemo(() => callNumberTypes.map(type => ({
id: type.id,
label: type.name,
})), [callNumberTypes]);

const formatRowData = useCallback(rowData => {
const callNumberType = rowData[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]
?.map(id => callNumberTypeOptions.find(type => type.id === id));

return {
...rowData,
name: intl.formatMessage({ id: `ui-inventory.settings.instanceCallNumber.${rowData.id}` }),
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: callNumberType,
};
}, [callNumberTypeOptions]);

const formatItemForSaving = useCallback((item) => ({
[CALL_NUMBER_BROWSE_COLUMNS.ID]: item[CALL_NUMBER_BROWSE_COLUMNS.ID],
[CALL_NUMBER_BROWSE_COLUMNS.SHELVING_ALGORITHM]: item[CALL_NUMBER_BROWSE_COLUMNS.SHELVING_ALGORITHM],
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: item[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS].map(type => type.id),
}), []);

if (!hasRequiredPermissions) {
return null;
}

if (isCentralTenantPermissionsLoading || isCallNumberTypesLoading) {
return <LoadingPane />;
}

return (
<TitleManager
page={intl.formatMessage({ id: 'ui-inventory.settings.inventory.title' })}
record={intl.formatMessage({ id: 'ui-inventory.callNumberTypes' })}
>
<ConnectedControlledVocab
stripes={stripes}
baseUrl="browse/config/instance-call-number"
records="configs"
objectLabel={null}
label={<FormattedMessage id="ui-inventory.callNumberBrowse" />}
labelSingular={intl.formatMessage({ id: 'ui-inventory.name' })}
visibleFields={[CALL_NUMBER_BROWSE_COLUMNS.NAME, CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]}
columnMapping={columnMapping}
hiddenFields={[CALL_NUMBER_BROWSE_COLUMNS.SHELVING_ALGORITHM, 'lastUpdated', 'numberOfObjects']}
formatter={{
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: CallNumberTypeList,
}}
readOnlyFields={[CALL_NUMBER_BROWSE_COLUMNS.NAME]}
nameKey="name"
formType="final-form"
id="call-number-browse"
preUpdateHook={formatItemForSaving}
editable
fieldComponents={{
[CALL_NUMBER_BROWSE_COLUMNS.TYPE_IDS]: (callNumberTypeProps) => (
<CallNumberTypeField
{...callNumberTypeProps}
fieldLabels={fieldLabels}
callNumberTypeOptions={callNumberTypeOptions}
/>
),
}}
canCreate={false}
parseRow={formatRowData}
actionSuppressor={{
delete: () => true,
edit: () => false,
}}
translations={{
termUpdated: 'ui-inventory.settings.instanceCallNumber.termUpdated'
}}
hideCreateButton
tenant={centralTenantId}
/>
</TitleManager>
);
};

export default CallNumberBrowseSettings;
Loading

0 comments on commit e2d8705

Please sign in to comment.