Skip to content

Commit

Permalink
Merge branch 'refs/heads/master' into UIBULKED-605
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
vashjs committed Jan 28, 2025
2 parents bcb8c78 + 38c1f8c commit 5f89b4a
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 124 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* [UIBULKED-599](https://folio-org.atlassian.net/browse/UIBULKED-599) Change Administrative note type is not supported for MARC instances.
* [UIBULKED-589](https://folio-org.atlassian.net/browse/UIBULKED-589) Make options in the "Actions" dropdown in "Bulk edits" in alphabetical order.
* [UIBULKED-588](https://folio-org.atlassian.net/browse/IBULKED-588) Displaying errors and warnings.
* [UIBULKED-570](https://folio-org.atlassian.net/browse/UIBULKED-570) Downloading files from Logs tab
* [UIBULKED-605](https://folio-org.atlassian.net/browse/UIBULKED-605) Enabling Confirm changes button based on forms state.

## [4.2.2](https://github.com/folio-org/ui-bulk-edit/tree/v4.2.2) (2024-11-15)
Expand Down
8 changes: 3 additions & 5 deletions src/components/BulkEditActionMenu/BulkEditActionMenu.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useOkapiKy } from '@folio/stripes/core';

import '../../../test/jest/__mock__';
import { runAxeTest } from '@folio/stripes-testing';
import { omit } from 'lodash';
import { bulkEditLogsData } from '../../../test/jest/__mock__/fakeData';
import { queryClient } from '../../../test/jest/utils/queryClient';

Expand All @@ -19,8 +20,8 @@ import {
CRITERIA,
EDITING_STEPS,
JOB_STATUSES,
FILE_KEYS,
FILE_SEARCH_PARAMS,
LINK_KEYS,
} from '../../constants';
import { useBulkOperationDetails } from '../../hooks/api';

Expand All @@ -36,10 +37,7 @@ const onToggle = jest.fn();
const bulkOperation = {
...bulkEditLogsData[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
...omit(LINK_KEYS, ['expired']),
};
const defaultProviderState = {
visibleColumns: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import React, {
useEffect,
} from 'react';
import PropTypes from 'prop-types';
import { saveAs } from 'file-saver';
import {
IconButton,
DropdownMenu,
Expand All @@ -16,12 +15,12 @@ import {
} from '@folio/stripes/components';
import { FormattedMessage } from 'react-intl';
import { QUERY_KEY_DOWNLOAD_LOGS, useFileDownload } from '../../../hooks/api';
import { APPROACHES, CAPABILITIES, linkNamesMap } from '../../../constants';
import { APPROACHES, CAPABILITIES, LINK_KEYS } from '../../../constants';
import { useBulkPermissions } from '../../../hooks';
import { getFileName } from '../../../utils/files';
import { savePreviewFile } from '../../../utils/files';

const BulkEditLogsActions = ({ item }) => {
const fileNamePostfix = item.fqlQueryId ? `.${APPROACHES.QUERY}` : '';
const fileNamePostfix = item?.fqlQueryId ? `.${APPROACHES.QUERY}` : '';

const {
hasUsersViewPerms,
Expand All @@ -33,9 +32,13 @@ const BulkEditLogsActions = ({ item }) => {
queryKey: QUERY_KEY_DOWNLOAD_LOGS,
enabled: false,
id: item.id,
fileContentType: linkNamesMap[triggeredFile],
onSuccess: data => {
saveAs(new Blob([data]), getFileName(item, triggeredFile));
fileContentType: LINK_KEYS[triggeredFile],
onSuccess: fileData => {
savePreviewFile({
fileName: item?.[triggeredFile],
fileData,
});

setTriggeredFile(null);
},
});
Expand All @@ -50,7 +53,7 @@ const BulkEditLogsActions = ({ item }) => {
setTriggeredFile(file);
};

const availableFiles = Object.keys(linkNamesMap).filter(linkName => item[linkName]);
const availableFiles = Object.keys(LINK_KEYS).filter(linkName => item[linkName]);

const renderTrigger = useCallback(({ triggerRef, onToggle, ariaProps, keyHandler }) => (
<IconButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,43 @@ import { render, screen } from '@testing-library/react';
import { QueryClientProvider } from 'react-query';
import { useOkapiKy } from '@folio/stripes/core';
import '../../../../test/jest/__mock__';
import { omit } from 'lodash';
import {
bulkEditLogsData,
bulkEditLogsDataWithExpiredFlag,
} from '../../../../test/jest/__mock__/fakeData';
import { queryClient } from '../../../../test/jest/utils/queryClient';
import {
JOB_STATUSES,
FILE_KEYS, CAPABILITIES,
FILE_KEYS,
CAPABILITIES,
LINK_KEYS,
} from '../../../constants';
import BulkEditLogsActions from './BulkEditLogsActions';
import { useBulkPermissions } from '../../../hooks';
import { useFileDownload } from '../../../hooks/api';

const linksWithoutExpired = omit(LINK_KEYS, ['expired']);

const bulkOperation = {
...bulkEditLogsData[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK]: FILE_KEYS.PROPOSED_CHANGES_LINK,
[FILE_KEYS.TRIGGERING_FILE]: FILE_KEYS.TRIGGERING_FILE,
[FILE_KEYS.PROPOSED_CHANGES_LINK_MARC]: FILE_KEYS.PROPOSED_CHANGES_LINK_MARC,
[FILE_KEYS.UPDATE_CHANGES_LINK_MARC]: FILE_KEYS.UPDATE_CHANGES_LINK_MARC,
...linksWithoutExpired,
};

const bulkOperationWithExpired = {
...bulkEditLogsDataWithExpiredFlag[0],
status: JOB_STATUSES.DATA_MODIFICATION,
[FILE_KEYS.MATCHING_RECORDS_LINK]: FILE_KEYS.MATCHING_RECORDS_LINK,
[FILE_KEYS.UPDATED_RECORDS_LINK]: FILE_KEYS.UPDATED_RECORDS_LINK,
[FILE_KEYS.MATCHING_ERRORS_LINK]: FILE_KEYS.MATCHING_ERRORS_LINK,
[FILE_KEYS.UPDATED_ERRORS_LINK]: FILE_KEYS.UPDATED_ERRORS_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK]: FILE_KEYS.PROPOSED_CHANGES_LINK,
[FILE_KEYS.PROPOSED_CHANGES_LINK_MARC]: FILE_KEYS.PROPOSED_CHANGES_LINK_MARC,
[FILE_KEYS.TRIGGERING_FILE]: FILE_KEYS.TRIGGERING_FILE,
...linksWithoutExpired,
};
jest.mock('../../../hooks', () => ({
...jest.requireActual('../../../hooks'),
useBulkPermissions: jest.fn(),
useFileDownload: jest.fn(),
}));

jest.mock('../../../hooks/api', () => ({
useFileDownload: jest.fn(),
}));

jest.mock('../../../hooks/useSearchParams', () => ({
Expand All @@ -57,6 +55,19 @@ const renderBulkEditLogsActions = ({ item = bulkOperation } = {}) => {
};

describe('BulkEditLogsActions', () => {
const mockPermissions = {
hasUsersViewPerms: true,
hasInventoryInstanceViewPerms: true,
};
const mockRefetch = jest.fn();

beforeEach(() => {
useBulkPermissions.mockReturnValue(mockPermissions);
useFileDownload.mockReturnValue({
refetch: mockRefetch,
});
});

beforeEach(() => {
useOkapiKy.mockClear().mockReturnValue({});
});
Expand Down
14 changes: 8 additions & 6 deletions src/components/BulkEditPane/BulkEditPane.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useCallback, useMemo, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { saveAs } from 'file-saver';

import {
Pane,
Expand All @@ -22,7 +21,7 @@ import {
CRITERIA,
APPROACHES,
FILE_SEARCH_PARAMS,
FILE_TO_LINK,
FILE_KEYS,
} from '../../constants';
import { RootContext } from '../../context/RootContext';
import { BulkEditLogs } from '../BulkEditLogs/BulkEditLogs';
Expand All @@ -38,6 +37,7 @@ import { useResetFilters } from '../../hooks/useResetFilters';

import { BulkEditInAppLayer } from './BulkEditInAppLayer/BulkEditInAppLayer';
import { BulkEditMarcLayer } from './BulkEditMarcLayer/BulkEditMarcLayer';
import { savePreviewFile } from '../../utils/files';

export const BulkEditPane = () => {
const [isFileUploaded, setIsFileUploaded] = useState(false);
Expand Down Expand Up @@ -103,10 +103,12 @@ export const BulkEditPane = () => {
queryKey: QUERY_KEY_DOWNLOAD_ACTION_MENU,
enabled: !!fileInfo,
id: bulkOperationId,
fileContentType: FILE_SEARCH_PARAMS[fileInfo?.param]?.replace('_MARC', ''),
onSuccess: data => {
/* istanbul ignore next */
saveAs(new Blob([data]), fileInfo?.bulkDetails[FILE_TO_LINK[fileInfo?.param]].split('/')[1]);
fileContentType: FILE_SEARCH_PARAMS[fileInfo?.param],
onSuccess: fileData => {
savePreviewFile({
fileData,
fileName: fileInfo?.bulkDetails[FILE_KEYS[fileInfo?.param]],
});
},
onSettled: () => {
/* istanbul ignore next */
Expand Down
48 changes: 26 additions & 22 deletions src/constants/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,69 @@ import { FormattedMessage } from 'react-intl';
import React from 'react';
import { EDITING_STEPS } from './core';

// use as marks that getFileName are ready
export const FILE_KEYS = {
MATCHING_RECORDS_LINK: 'linkToMatchedRecordsCsvFile',
MATCHING_ERRORS_LINK: 'linkToMatchedRecordsErrorsCsvFile',
PROPOSED_CHANGES_LINK: 'linkToModifiedRecordsCsvFile',
PROPOSED_CHANGES_LINK_MARC: 'linkToModifiedRecordsMarcFile',
UPDATE_CHANGES_LINK_MARC: 'linkToCommittedRecordsMarcFile',
UPDATED_RECORDS_LINK: 'linkToCommittedRecordsCsvFile',
UPDATED_ERRORS_LINK: 'linkToCommittedRecordsErrorsCsvFile',
TRIGGERING_FILE: 'linkToTriggeringCsvFile',
};

// use as API key for /download
export const FILE_SEARCH_PARAMS = {
TRIGGERING_FILE: 'TRIGGERING_FILE',
MATCHED_RECORDS_FILE: 'MATCHED_RECORDS_FILE',
RECORD_MATCHING_ERROR_FILE: 'RECORD_MATCHING_ERROR_FILE',
COMMITTED_RECORDS_FILE: 'COMMITTED_RECORDS_FILE',
COMMITTED_RECORDS_MARC_FILE: 'COMMITTED_RECORDS_MARC_FILE',
COMMITTING_CHANGES_ERROR_FILE: 'COMMITTING_CHANGES_ERROR_FILE',
PROPOSED_CHANGES_FILE: 'PROPOSED_CHANGES_FILE',
PROPOSED_CHANGES_MARC_FILE: 'PROPOSED_CHANGES_MARC_FILE',
COMMITTED_RECORDS_FILE_MARC: 'COMMITTED_RECORDS_FILE_MARC',
};

export const FILE_TO_LINK = {
// use as marks that getFileName are ready
export const FILE_KEYS = {
TRIGGERING_FILE: 'linkToTriggeringCsvFile',
MATCHED_RECORDS_FILE: 'linkToMatchedRecordsCsvFile',
RECORD_MATCHING_ERROR_FILE: 'linkToMatchedRecordsErrorsCsvFile',
COMMITTED_RECORDS_FILE: 'linkToCommittedRecordsCsvFile',
COMMITTED_RECORDS_MARC_FILE: 'linkToCommittedRecordsMarcFile',
COMMITTING_CHANGES_ERROR_FILE: 'linkToCommittedRecordsErrorsCsvFile',
PROPOSED_CHANGES_FILE: 'linkToModifiedRecordsCsvFile',
COMMITTED_RECORDS_FILE_MARC: 'linkToCommittedRecordsMarcFile'
PROPOSED_CHANGES_MARC_FILE: 'linkToModifiedRecordsMarcFile',
};

export const LINK_KEYS = {
linkToTriggeringCsvFile: FILE_SEARCH_PARAMS.TRIGGERING_FILE,
linkToMatchedRecordsCsvFile: FILE_SEARCH_PARAMS.MATCHED_RECORDS_FILE,
linkToMatchedRecordsErrorsCsvFile: FILE_SEARCH_PARAMS.RECORD_MATCHING_ERROR_FILE,
linkToModifiedRecordsCsvFile: FILE_SEARCH_PARAMS.PROPOSED_CHANGES_FILE,
linkToModifiedRecordsMarcFile: FILE_SEARCH_PARAMS.PROPOSED_CHANGES_MARC_FILE,
linkToCommittedRecordsCsvFile: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE,
linkToCommittedRecordsMarcFile: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_MARC_FILE,
linkToCommittedRecordsErrorsCsvFile: FILE_SEARCH_PARAMS.COMMITTING_CHANGES_ERROR_FILE,
expired: 'expired',
};

export const getDownloadLinks = ({ perms, step }) => [
{
KEY: FILE_KEYS.MATCHING_RECORDS_LINK,
KEY: FILE_KEYS.MATCHED_RECORDS_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.MATCHED_RECORDS_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadMathcedRecords" />,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadMatchedRecords" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.UPLOAD,
},
{
KEY: FILE_KEYS.UPDATED_RECORDS_LINK,
KEY: FILE_KEYS.COMMITTED_RECORDS_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadChangedRecords" />,
IS_VISIBLE: perms.hasAnyEditPermissions,
},
{
KEY: FILE_KEYS.UPDATE_CHANGES_LINK_MARC,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_FILE_MARC,
KEY: FILE_KEYS.COMMITTED_RECORDS_MARC_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTED_RECORDS_MARC_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadChangedRecords.marc" />,
IS_VISIBLE: perms.hasAnyEditPermissions,
},
{
KEY: FILE_KEYS.MATCHING_ERRORS_LINK,
KEY: FILE_KEYS.RECORD_MATCHING_ERROR_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.RECORD_MATCHING_ERROR_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadErrors" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.UPLOAD,
},
{
KEY: FILE_KEYS.UPDATED_ERRORS_LINK,
KEY: FILE_KEYS.COMMITTING_CHANGES_ERROR_FILE,
SEARCH_PARAM: FILE_SEARCH_PARAMS.COMMITTING_CHANGES_ERROR_FILE,
LINK_NAME: <FormattedMessage id="ui-bulk-edit.start.downloadErrors" />,
IS_VISIBLE: perms.hasAnyEditPermissions && step === EDITING_STEPS.COMMIT,
Expand Down
1 change: 0 additions & 1 deletion src/constants/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ export * from './columns';
export * from './inAppActions';
export * from '../utils/date';
export * from './files';
export * from './logsActions';
export * from './moduleNames';
11 changes: 0 additions & 11 deletions src/constants/logsActions.js

This file was deleted.

20 changes: 0 additions & 20 deletions src/utils/files.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,5 @@
import { saveAs } from 'file-saver';

import { getFormattedFilePrefixDate } from './date';


export const getFileName = (item, triggeredFile) => {
if (item.fqlQueryId) {
return {
linkToTriggeringCsvFile: `Query-${item.id}.csv`,
linkToMatchedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Matched-Records-Query-${item.id}.csv`,
linkToModifiedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Updates-Preview-Query-${item.id}.csv`,
linkToModifiedRecordsMarcFile: `${getFormattedFilePrefixDate()}-Updates-Preview-Query-${item.id}.mrc`,
linkToCommittedRecordsCsvFile: `${getFormattedFilePrefixDate()}-Changed-Records-Query-${item.id}.csv`,
linkToCommittedRecordsMarcFile: `${getFormattedFilePrefixDate()}-Changed-Records-Query-${item.id}.mrc`,
linkToCommittedRecordsErrorsCsvFile: `${getFormattedFilePrefixDate()}-Committing-changes-Errors-Query-${item.id}.csv`,
linkToMatchedRecordsErrorsCsvFile:`${getFormattedFilePrefixDate()}-Matching-Records-Errors-Query-${item.id}.csv`,
}[triggeredFile];
}

return item[triggeredFile].split('/')[1];
};

export const changeExtension = (fileName, extension) => {
if (!fileName) return fileName;

Expand Down
25 changes: 1 addition & 24 deletions src/utils/files.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { saveAs } from 'file-saver';

import { getFileName, changeExtension, savePreviewFile } from './files';
import { changeExtension, savePreviewFile } from './files';
import { getFormattedFilePrefixDate } from './date';


Expand All @@ -14,29 +14,6 @@ jest.mock('file-saver', () => ({


describe('files', () => {
describe('getFileName', () => {
it('should return the correct file name for Query approach - linkToTriggeringCsvFile', () => {
const item = { fqlQueryId: '111', id: 123 };
const triggeredFile = 'linkToTriggeringCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('Query-123.csv');
});

it('should return the correct file name for Query approach - linkToMatchedRecordsCsvFile', () => {
const item = { fqlQueryId: '111', id: 123 };
const triggeredFile = 'linkToMatchedRecordsCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('mockedDate-Matched-Records-Query-123.csv');
});

it('should return the correct file name for non-Query approach', () => {
const item = { fqlQueryId: null, linkToTriggeringCsvFile: 'somePath/someFile.csv' };
const triggeredFile = 'linkToTriggeringCsvFile';
const result = getFileName(item, triggeredFile);
expect(result).toBe('someFile.csv');
});
});

describe('changeExtension', () => {
it('should change the extension of a file', () => {
expect(changeExtension('abc.csv', 'mrc')).toBe('abc.mrc');
Expand Down
4 changes: 2 additions & 2 deletions src/utils/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { NoValue } from '@folio/stripes/components';
import { FolioFormattedTime } from '@folio/stripes-acq-components';

import BulkEditLogsActions from '../components/BulkEditLogs/BulkEditLogsActions/BulkEditLogsActions';
import { linkNamesMap } from '../constants';
import { LINK_KEYS } from '../constants';

const isActionsRendered = (item) => Object.keys(item).some(key => Object.keys(linkNamesMap).includes(key));
const isActionsRendered = (item) => Object.keys(item).some(key => Object.keys(LINK_KEYS).includes(key));

export const getLogsResultsFormatter = () => ({
id: item => item.id,
Expand Down
Loading

0 comments on commit 5f89b4a

Please sign in to comment.