From e568ed6b504431d4f46ceae530afbe24c490bf24 Mon Sep 17 00:00:00 2001 From: vashjs Date: Thu, 25 Jan 2024 15:08:23 +0100 Subject: [PATCH 1/7] UIBULKED-246 Displaying and paginating through retrieved records --- .../BulkEditInAppPreviewModal.js | 19 ++++++- .../BulkEditListResult/Preview/Preview.js | 1 + .../PreviewAccordion/PreviewAccordion.js | 57 +++++++++++++------ src/constants/core.js | 2 +- 4 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js index 9eba3409..94fa44fc 100644 --- a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js +++ b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js @@ -7,7 +7,7 @@ import { saveAs } from 'file-saver'; import { MessageBanner, Modal, MultiColumnList } from '@folio/stripes/components'; import { Preloader } from '@folio/stripes-data-transfer-components'; -import { useShowCallout } from '@folio/stripes-acq-components'; +import { PrevNextPagination, usePagination, useShowCallout } from '@folio/stripes-acq-components'; import { RootContext } from '../../../../context/RootContext'; import { @@ -15,7 +15,7 @@ import { EDITING_STEPS, FILE_KEYS, FILE_SEARCH_PARAMS, - getFormattedFilePrefixDate, + getFormattedFilePrefixDate, LOGS_PAGINATION_CONFIG, } from '../../../../constants'; import { useRecordsPreview, @@ -60,7 +60,13 @@ const BulkEditInAppPreviewModal = ({ const { contentUpdate } = useContentUpdate({ id: bulkOperationId }); const { bulkOperationStart } = useBulkOperationStart(); + const { + pagination, + changePage, + } = usePagination(LOGS_PAGINATION_CONFIG); + const [isPreviewLoading, setIsLoadingPreview] = useState(false); + const { contentData, columnMapping, @@ -183,6 +189,15 @@ const BulkEditInAppPreviewModal = ({ columnIdPrefix="in-app" columnWidths={PREVIEW_COLUMN_WIDTHS} /> + + {contentData.length > 0 && ( + + )} ) : } diff --git a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js index 41f78373..5c703cf0 100644 --- a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js +++ b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js @@ -76,6 +76,7 @@ export const Preview = ({ id, title, isInitial, bulkDetails }) => {
{Boolean(contentData?.length) && ( { +const PreviewAccordion = ({ + contentData, + columnMapping, + visibleColumns, + isInitial, + step, + totalRecords, +}) => { const translationKey = isInitial ? 'title' : 'titleChanged'; const accordionLabel = ; const visibleColumnKeys = getVisibleColumnsKeys(visibleColumns); + const { + pagination, + changePage, + } = usePagination(LOGS_PAGINATION_CONFIG); + return ( -
- - +
+ + + +
+ {contentData.length > 0 && ( + -
-
+ )} + ); }; PreviewAccordion.propTypes = { + totalRecords: PropTypes.number, contentData: PropTypes.arrayOf(PropTypes.object), columnMapping: PropTypes.object, visibleColumns: PropTypes.arrayOf(PropTypes.object), diff --git a/src/constants/core.js b/src/constants/core.js index bd5fadf8..4506b3e5 100644 --- a/src/constants/core.js +++ b/src/constants/core.js @@ -82,7 +82,7 @@ export const CONTROL_TYPES = { }; export const TRANSLATION_SUFFIX = { - [CAPABILITIES.USER]: '', + [CAPABILITIES.USER]: '.users', [CAPABILITIES.ITEM]: '.item', [CAPABILITIES.INSTANCE]: '.instance', [CAPABILITIES.HOLDING]: '.holdings', From efeabd3e780506d7847bc84c6f88858d36d70566 Mon Sep 17 00:00:00 2001 From: vashjs Date: Sun, 28 Jan 2024 23:33:57 +0100 Subject: [PATCH 2/7] UIBULKED-246 Displaying and paginating through retrieved records --- CHANGELOG.md | 1 + .../BulkEditInAppPreviewModal.js | 8 ++- .../ErrorsAccordion/ErrorsAccordion.js | 2 +- .../BulkEditListResult/Preview/Preview.css | 15 ++-- .../BulkEditListResult/Preview/Preview.js | 51 +++++++------- .../PreviewAccordion/PreviewAccordion.js | 70 ++++++++++--------- src/components/BulkEditLogs/BulkEditLogs.js | 4 +- src/constants/core.js | 4 +- src/hooks/api/useContentUpdate.js | 5 +- src/hooks/api/useRecordsPreview.js | 16 ++--- src/hooks/useBulkOperationStats.js | 32 +++++++++ src/hooks/usePagination.js | 14 ++++ 12 files changed, 140 insertions(+), 82 deletions(-) create mode 100644 src/hooks/useBulkOperationStats.js create mode 100644 src/hooks/usePagination.js diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a83d980..b47b6612 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ * [UIBULKED-364](https://issues.folio.org/browse/UIBULKED-364) Instance records - preview of matching records. * [UIBULKED-389](https://issues.folio.org/browse/UIBULKED-389) Add Suppress from discovery option to Instance records edits. * [UIBULKED-400](https://issues.folio.org/browse/UIBULKED-400) Enabling Build query button on Query tab. +* [UIBULKED-246](https://issues.folio.org/browse/UIBULKED-246) Enabling Build query button on Query tab. ## [4.0.0](https://github.com/folio-org/ui-bulk-edit/tree/v4.0.0) (2023-10-12) diff --git a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js index 94fa44fc..341594b7 100644 --- a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js +++ b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js @@ -7,7 +7,7 @@ import { saveAs } from 'file-saver'; import { MessageBanner, Modal, MultiColumnList } from '@folio/stripes/components'; import { Preloader } from '@folio/stripes-data-transfer-components'; -import { PrevNextPagination, usePagination, useShowCallout } from '@folio/stripes-acq-components'; +import { PrevNextPagination, useShowCallout } from '@folio/stripes-acq-components'; import { RootContext } from '../../../../context/RootContext'; import { @@ -15,7 +15,7 @@ import { EDITING_STEPS, FILE_KEYS, FILE_SEARCH_PARAMS, - getFormattedFilePrefixDate, LOGS_PAGINATION_CONFIG, + getFormattedFilePrefixDate, PAGINATION_CONFIG, } from '../../../../constants'; import { useRecordsPreview, @@ -33,6 +33,7 @@ import { BulkEditInAppPreviewModalFooter } from './BulkEditInAppPreviewModalFoot import css from './BulkEditInAppPreviewModal.css'; import { getVisibleColumnsKeys } from '../../../../utils/helpers'; import { PREVIEW_COLUMN_WIDTHS } from '../../../PermissionsModal/constants/lists'; +import { usePagination } from '../../../../hooks/usePagination'; const BulkEditInAppPreviewModal = ({ open, @@ -63,7 +64,7 @@ const BulkEditInAppPreviewModal = ({ const { pagination, changePage, - } = usePagination(LOGS_PAGINATION_CONFIG); + } = usePagination(PAGINATION_CONFIG); const [isPreviewLoading, setIsLoadingPreview] = useState(false); @@ -83,6 +84,7 @@ const BulkEditInAppPreviewModal = ({ onKeepEditing(); }, }, + ...pagination, }); const { refetch } = useFileDownload({ diff --git a/src/components/BulkEditList/BulkEditListResult/Preview/ErrorsAccordion/ErrorsAccordion.js b/src/components/BulkEditList/BulkEditListResult/Preview/ErrorsAccordion/ErrorsAccordion.js index 311986b8..1a244619 100644 --- a/src/components/BulkEditList/BulkEditListResult/Preview/ErrorsAccordion/ErrorsAccordion.js +++ b/src/components/BulkEditList/BulkEditListResult/Preview/ErrorsAccordion/ErrorsAccordion.js @@ -57,7 +57,7 @@ const ErrorsAccordion = ({ }} label={} > -
+
{headLine} diff --git a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.css b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.css index 42c5a8b8..29e0ca08 100644 --- a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.css +++ b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.css @@ -5,11 +5,12 @@ } .previewAccordion, +.previewAccordionList, .errorAccordionList { flex-grow: 1; } -.previewAccordion:first-child:has([aria-expanded="false"]) { +.previewAccordion:has([aria-expanded="false"]) { flex-grow: 0; } @@ -17,13 +18,19 @@ height: 100%; } -.previewAccordionInner, -.errorAccordionInner { +.previewAccordionInner { + display: flex; + flex-direction: column; + height: 100%; +} + +.previewAccordionOuter, +.errorAccordionOuter { display: flex; flex-direction: column; flex-grow: 1; } -.errorAccordionInner { +.errorAccordionOuter { height: 100%; } diff --git a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js index 5c703cf0..e86f0edd 100644 --- a/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js +++ b/src/components/BulkEditList/BulkEditListResult/Preview/Preview.js @@ -5,7 +5,6 @@ import { MessageBanner, } from '@folio/stripes/components'; import PropTypes from 'prop-types'; -import { useContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router-dom'; import css from './Preview.css'; import { PreviewAccordion } from './PreviewAccordion'; @@ -15,45 +14,40 @@ import { useErrorsPreview, useRecordsPreview } from '../../../../hooks/api'; -import { RootContext } from '../../../../context/RootContext'; -import { EDITING_STEPS } from '../../../../constants'; +import { PAGINATION_CONFIG } from '../../../../constants'; +import { usePagination } from '../../../../hooks/usePagination'; +import { useBulkOperationStats } from '../../../../hooks/useBulkOperationStats'; export const Preview = ({ id, title, isInitial, bulkDetails }) => { const location = useLocation(); - const { countOfRecords, setCountOfRecords, visibleColumns } = useContext(RootContext); - const [countOfErrors, setCountOfErrors] = useState(0); - const [totalCount, setTotalCount] = useState(0); - const search = new URLSearchParams(location.search); const step = search.get('step'); const capabilities = search.get('capabilities'); - const { contentData, columns, columnMapping } = useRecordsPreview({ + const { + countOfRecords, + countOfErrors, + totalCount, + visibleColumns, + } = useBulkOperationStats({ bulkDetails, step }); + + const { + pagination, + changePage, + } = usePagination(PAGINATION_CONFIG); + + const { contentData, columns, columnMapping, isFetching } = useRecordsPreview({ key: RECORDS_PREVIEW_KEY, id, step, - capabilities + capabilities, + ...pagination, }); - const { data } = useErrorsPreview({ id }); - const errors = data?.errors || []; - useEffect(() => { - const isInitialPreview = step === EDITING_STEPS.UPLOAD; - - const countRecords = isInitialPreview - ? bulkDetails.matchedNumOfRecords - : bulkDetails.committedNumOfRecords; - - const countErrors = isInitialPreview - ? bulkDetails.matchedNumOfErrors - : bulkDetails.committedNumOfErrors; - - setCountOfErrors(countErrors); - setCountOfRecords(countRecords); - setTotalCount(isInitialPreview ? bulkDetails.totalNumOfRecords : bulkDetails.matchedNumOfRecords); - }, [bulkDetails, step]); + const { data } = useErrorsPreview({ id }); + const errors = data?.errors || []; return ( @@ -73,7 +67,7 @@ export const Preview = ({ id, title, isInitial, bulkDetails }) => { {title} )} -
+
{Boolean(contentData?.length) && ( { columnMapping={columnMapping} visibleColumns={visibleColumns} step={step} + onChangePage={changePage} + pagination={pagination} + isFetching={isFetching} /> )} diff --git a/src/components/BulkEditList/BulkEditListResult/Preview/PreviewAccordion/PreviewAccordion.js b/src/components/BulkEditList/BulkEditListResult/Preview/PreviewAccordion/PreviewAccordion.js index 4d55984e..ae87ffcd 100644 --- a/src/components/BulkEditList/BulkEditListResult/Preview/PreviewAccordion/PreviewAccordion.js +++ b/src/components/BulkEditList/BulkEditListResult/Preview/PreviewAccordion/PreviewAccordion.js @@ -5,11 +5,10 @@ import { Accordion, MultiColumnList, } from '@folio/stripes/components'; -import { PrevNextPagination, usePagination } from '@folio/stripes-acq-components'; +import { PrevNextPagination } from '@folio/stripes-acq-components'; import { PREVIEW_COLUMN_WIDTHS } from '../../../../PermissionsModal/constants/lists'; import { getVisibleColumnsKeys } from '../../../../../utils/helpers'; import css from '../Preview.css'; -import { LOGS_PAGINATION_CONFIG } from '../../../../../constants'; const PreviewAccordion = ({ @@ -19,6 +18,9 @@ const PreviewAccordion = ({ isInitial, step, totalRecords, + pagination, + onChangePage, + isFetching, }) => { const translationKey = isInitial ? 'title' : 'titleChanged'; @@ -26,37 +28,35 @@ const PreviewAccordion = ({ const visibleColumnKeys = getVisibleColumnsKeys(visibleColumns); - const { - pagination, - changePage, - } = usePagination(LOGS_PAGINATION_CONFIG); - return ( - <> -
- - - -
- {contentData.length > 0 && ( - - )} - +
+ +
+
+ +
+ {contentData.length > 0 && ( + + )} +
+
+
); }; @@ -67,6 +67,12 @@ PreviewAccordion.propTypes = { visibleColumns: PropTypes.arrayOf(PropTypes.object), isInitial: PropTypes.bool, step: PropTypes.string, + pagination: PropTypes.shape({ + offset: PropTypes.number, + limit: PropTypes.number, + }), + onChangePage: PropTypes.func, + isFetching: PropTypes.bool, }; export default memo(PreviewAccordion); diff --git a/src/components/BulkEditLogs/BulkEditLogs.js b/src/components/BulkEditLogs/BulkEditLogs.js index 9e13178b..1de512a5 100644 --- a/src/components/BulkEditLogs/BulkEditLogs.js +++ b/src/components/BulkEditLogs/BulkEditLogs.js @@ -16,7 +16,7 @@ import { SORTING_PARAMETER, } from '@folio/stripes-acq-components'; -import { LOGS_COLUMNS, LOGS_PAGINATION_CONFIG } from '../../constants'; +import { LOGS_COLUMNS, PAGINATION_CONFIG } from '../../constants'; import { getLogsResultsFormatter } from '../../utils/formatters'; import { useLogsQueryParams } from '../../hooks'; import { useBulkEditLogs } from '../../hooks/api'; @@ -49,7 +49,7 @@ const BulkEditLogs = () => { const { pagination, changePage, - } = usePagination(LOGS_PAGINATION_CONFIG); + } = usePagination(PAGINATION_CONFIG); const { logs, diff --git a/src/constants/core.js b/src/constants/core.js index 4506b3e5..8c9402d2 100644 --- a/src/constants/core.js +++ b/src/constants/core.js @@ -4,7 +4,7 @@ export const BULK_VISIBLE_COLUMNS = 'bulk-edit-visible-columns'; export const PREVIEW_LIMITS = { ERRORS: 10, - RECORDS: 10, + RECORDS: 100, }; export const APPROACHES = { @@ -104,7 +104,7 @@ export const MANUAL_UPLOAD_STEPS = { CONFIRM: 'CONFIRM', }; -export const LOGS_PAGINATION_CONFIG = { +export const PAGINATION_CONFIG = { limit: 100, offset: 0, }; diff --git a/src/hooks/api/useContentUpdate.js b/src/hooks/api/useContentUpdate.js index 24bd3c7c..364abe72 100644 --- a/src/hooks/api/useContentUpdate.js +++ b/src/hooks/api/useContentUpdate.js @@ -1,14 +1,13 @@ import { useMutation } from 'react-query'; import { useOkapiKy } from '@folio/stripes/core'; -import { PREVIEW_LIMITS } from '../../constants'; -export const useContentUpdate = ({ id }) => { +export const useContentUpdate = ({ id, limit, offset }) => { const ky = useOkapiKy(); const { data, mutateAsync: contentUpdate, isLoading } = useMutation({ mutationFn: ({ contentUpdates }) => { return ky.post(`bulk-operations/${id}/content-update`, { - searchParams: { limit: PREVIEW_LIMITS.RECORDS }, + searchParams: { limit, offset }, json: contentUpdates, }); }, diff --git a/src/hooks/api/useRecordsPreview.js b/src/hooks/api/useRecordsPreview.js index 6eb0fde5..65ceb3cd 100644 --- a/src/hooks/api/useRecordsPreview.js +++ b/src/hooks/api/useRecordsPreview.js @@ -4,10 +4,7 @@ import { useIntl } from 'react-intl'; import { useOkapiKy } from '@folio/stripes/core'; -import { - BULK_VISIBLE_COLUMNS, - PREVIEW_LIMITS, -} from '../../constants'; +import { BULK_VISIBLE_COLUMNS } from '../../constants'; import { getMappedTableData } from '../../utils/mappers'; import { RootContext } from '../../context/RootContext'; @@ -20,17 +17,20 @@ export const useRecordsPreview = ({ step, queryOptions, capabilities, + limit, + offset, }) => { const intl = useIntl(); const { setVisibleColumns } = useContext(RootContext); const ky = useOkapiKy(); - const { data, refetch, isLoading } = useQuery( + const { data, refetch, isLoading, isFetching } = useQuery( { - queryKey: [key, id, step], + queryKey: [key, id, step, limit, offset], cacheTime: 0, + keepPreviousData: true, queryFn: () => { - return ky.get(`bulk-operations/${id}/preview`, { searchParams: { limit: PREVIEW_LIMITS.RECORDS, step } }).json(); + return ky.get(`bulk-operations/${id}/preview`, { searchParams: { limit, offset, step } }).json(); }, ...queryOptions, }, @@ -66,7 +66,7 @@ export const useRecordsPreview = ({ return { isLoading, refetch, - + isFetching, contentData, columnMapping, columns, diff --git a/src/hooks/useBulkOperationStats.js b/src/hooks/useBulkOperationStats.js new file mode 100644 index 00000000..8fe349fa --- /dev/null +++ b/src/hooks/useBulkOperationStats.js @@ -0,0 +1,32 @@ +import { useContext, useEffect, useState } from 'react'; +import { EDITING_STEPS } from '../constants'; +import { RootContext } from '../context/RootContext'; + +export const useBulkOperationStats = ({ bulkDetails, step }) => { + const { countOfRecords, setCountOfRecords, visibleColumns } = useContext(RootContext); + const [countOfErrors, setCountOfErrors] = useState(0); + const [totalCount, setTotalCount] = useState(0); + + useEffect(() => { + const isInitialPreview = step === EDITING_STEPS.UPLOAD; + + const countRecords = isInitialPreview + ? bulkDetails.matchedNumOfRecords + : bulkDetails.committedNumOfRecords; + + const countErrors = isInitialPreview + ? bulkDetails.matchedNumOfErrors + : bulkDetails.committedNumOfErrors; + + setCountOfErrors(countErrors); + setCountOfRecords(countRecords); + setTotalCount(isInitialPreview ? bulkDetails.totalNumOfRecords : bulkDetails.matchedNumOfRecords); + }, [bulkDetails, step]); + + return { + countOfRecords, + countOfErrors, + totalCount, + visibleColumns, + }; +}; diff --git a/src/hooks/usePagination.js b/src/hooks/usePagination.js new file mode 100644 index 00000000..bc5cb2f4 --- /dev/null +++ b/src/hooks/usePagination.js @@ -0,0 +1,14 @@ +import { useState, useCallback } from 'react'; + +export const usePagination = ({ limit, offset }) => { + const [pagination, setPagination] = useState({ offset, limit }); + + const changePage = useCallback((newPagination) => { + setPagination((p) => ({ ...p, ...newPagination })); + }, []); + + return { + pagination, + changePage, + }; +}; From 30c051b0db8797c6edbcf1aa7304551ad0dc83f6 Mon Sep 17 00:00:00 2001 From: vashjs Date: Mon, 29 Jan 2024 10:01:20 +0100 Subject: [PATCH 3/7] changed constants --- .../BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js | 3 ++- src/constants/core.js | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js index 341594b7..9b8a4ee8 100644 --- a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js +++ b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js @@ -15,7 +15,8 @@ import { EDITING_STEPS, FILE_KEYS, FILE_SEARCH_PARAMS, - getFormattedFilePrefixDate, PAGINATION_CONFIG, + PAGINATION_CONFIG, + getFormattedFilePrefixDate, } from '../../../../constants'; import { useRecordsPreview, diff --git a/src/constants/core.js b/src/constants/core.js index 2d305c1f..7ff94d3d 100644 --- a/src/constants/core.js +++ b/src/constants/core.js @@ -110,6 +110,6 @@ export const MANUAL_UPLOAD_STEPS = { }; export const PAGINATION_CONFIG = { - limit: 100, + limit: PREVIEW_LIMITS.RECORDS, offset: 0, }; From fc11e501df2d284abd47b4145cf80d9412ca0e62 Mon Sep 17 00:00:00 2001 From: vashjs Date: Mon, 29 Jan 2024 10:59:35 +0100 Subject: [PATCH 4/7] update in-app behavior --- .../BulkEditInAppPreviewModal.js | 11 +++++++---- src/hooks/api/useBulkOperationDetails.js | 4 +++- src/hooks/useResetAppState.js | 3 ++- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js index 9b8a4ee8..a4e89d95 100644 --- a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js +++ b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.js @@ -26,6 +26,7 @@ import { useFileDownload, QUERY_KEY_DOWNLOAD_IN_APP, IN_APP_PREVIEW_KEY, + BULK_OPERATION_DETAILS_KEY, } from '../../../../hooks/api'; import { getContentUpdatesBody } from '../BulkEditInApp/ContentUpdatesForm/helpers'; @@ -72,14 +73,13 @@ const BulkEditInAppPreviewModal = ({ const { contentData, columnMapping, - refetch: fetchPreview, + isFetching } = useRecordsPreview({ key: IN_APP_PREVIEW_KEY, id: bulkOperationId, step: EDITING_STEPS.EDIT, capabilities, queryOptions: { - enabled: false, onError: () => { swwCallout(); onKeepEditing(); @@ -146,8 +146,10 @@ const BulkEditInAppPreviewModal = ({ approach: APPROACHES.IN_APP, step: EDITING_STEPS.EDIT, })) - .then(() => fetchPreview()) - .then(() => queryClient.invalidateQueries('bulkOperationDetails')) + .then(() => { + queryClient.invalidateQueries(BULK_OPERATION_DETAILS_KEY); + queryClient.invalidateQueries(IN_APP_PREVIEW_KEY); + }) .catch(() => { swwCallout(); onKeepEditing(); @@ -191,6 +193,7 @@ const BulkEditInAppPreviewModal = ({ maxHeight={300} columnIdPrefix="in-app" columnWidths={PREVIEW_COLUMN_WIDTHS} + loading={isFetching} /> {contentData.length > 0 && ( diff --git a/src/hooks/api/useBulkOperationDetails.js b/src/hooks/api/useBulkOperationDetails.js index 931be718..0cc8b409 100644 --- a/src/hooks/api/useBulkOperationDetails.js +++ b/src/hooks/api/useBulkOperationDetails.js @@ -4,6 +4,8 @@ import { useOkapiKy } from '@folio/stripes/core'; import { useHistory } from 'react-router-dom'; import { buildSearch } from '@folio/stripes-acq-components'; +export const BULK_OPERATION_DETAILS_KEY = 'bulkOperationDetails'; + export const useBulkOperationDetails = ({ id, interval = 0, @@ -16,7 +18,7 @@ export const useBulkOperationDetails = ({ const [refetchInterval, setRefetchInterval] = useState(interval); const { data, isLoading } = useQuery({ - queryKey: ['bulkOperationDetails', id, refetchInterval, ...additionalQueryKeys], + queryKey: [BULK_OPERATION_DETAILS_KEY, id, refetchInterval, ...additionalQueryKeys], enabled: !!id, refetchInterval, queryFn: () => ky.get(`bulk-operations/${id}`).json(), diff --git a/src/hooks/useResetAppState.js b/src/hooks/useResetAppState.js index 7ae7f5b3..006b66cb 100644 --- a/src/hooks/useResetAppState.js +++ b/src/hooks/useResetAppState.js @@ -2,6 +2,7 @@ import { useEffect } from 'react'; import { buildSearch } from '@folio/stripes-acq-components'; import { useHistory } from 'react-router-dom'; import { useQueryClient } from 'react-query'; +import { BULK_OPERATION_DETAILS_KEY } from './api'; export const useResetAppState = ({ setFilters, @@ -27,7 +28,7 @@ export const useResetAppState = ({ setFilters(initialFiltersState); // clear bulkOperation information - queryClient.setQueryData('bulkOperationDetails', () => ({ data: undefined })); + queryClient.setQueryData(BULK_OPERATION_DETAILS_KEY, () => ({ data: undefined })); // reset confirmed file name setConfirmedFileName(null); From 6bc99226d55d9251d4231efb1ce7a28cc587fba9 Mon Sep 17 00:00:00 2001 From: vashjs Date: Mon, 29 Jan 2024 11:30:16 +0100 Subject: [PATCH 5/7] update tests --- .../BulkEditInAppPreviewModal.test.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.test.js b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.test.js index b784d1fb..46a39435 100644 --- a/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.test.js +++ b/src/components/BulkEditList/BulkEditListResult/BulkEditInAppPreviewModal/BulkEditInAppPreviewModal.test.js @@ -17,12 +17,14 @@ import { } from '../../../../constants'; import { useBulkOperationDetails, + useRecordsPreview, } from '../../../../hooks/api'; import BulkEditInAppPreviewModal from './BulkEditInAppPreviewModal'; jest.mock('../../../../hooks/api', () => ({ ...jest.requireActual('../../../../hooks/api'), useBulkOperationDetails: jest.fn(), + useRecordsPreview: jest.fn(), })); const bulkOperation = bulkEditLogsData[0]; @@ -63,6 +65,15 @@ describe('BulkEditInAppPreviewModal', () => { bulkDetails: bulkOperation, }); + useRecordsPreview.mockClear().mockReturnValue({ + isLoading: false, + refetch: jest.fn(), + isFetching: false, + contentData: [], + columnMapping: {}, + columns: [], + }); + useOkapiKy .mockClear() .mockReturnValue({ From 6743c544fc9ecebd16c567b231e6746028854d2b Mon Sep 17 00:00:00 2001 From: vashjs Date: Tue, 30 Jan 2024 12:40:35 +0100 Subject: [PATCH 6/7] avoid unnecessary preview calls --- src/components/BulkEditList/BulkEditList.js | 16 +++++++++------- .../BulkEditInAppPreviewModal.js | 15 +++++++++------ src/hooks/api/useBulkOperationStart.js | 3 ++- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/BulkEditList/BulkEditList.js b/src/components/BulkEditList/BulkEditList.js index d61a5ff6..d024fe16 100644 --- a/src/components/BulkEditList/BulkEditList.js +++ b/src/components/BulkEditList/BulkEditList.js @@ -222,13 +222,15 @@ export const BulkEditList = () => { /> - + {isPreviewModalOpened && ( + + )} { swwCallout(); onKeepEditing(); @@ -95,7 +98,7 @@ const BulkEditInAppPreviewModal = ({ fileInfo: { fileContentType: FILE_SEARCH_PARAMS.PROPOSED_CHANGES_FILE, }, - onSuccess: data => { + onSuccess: fileData => { const searchParams = new URLSearchParams(history.location.search); let fileName = searchParams.get('fileName'); @@ -103,7 +106,7 @@ const BulkEditInAppPreviewModal = ({ fileName = `${capabilities}-${searchParams.get('criteria')}.csv`; } - saveAs(new Blob([data]), `${getFormattedFilePrefixDate()}-Updates-Preview-${fileName}`); + saveAs(new Blob([fileData]), `${getFormattedFilePrefixDate()}-Updates-Preview-${fileName}`); }, }); @@ -131,11 +134,11 @@ const BulkEditInAppPreviewModal = ({ }; useEffect(() => { - if (contentUpdates && open) { + if (contentUpdates && open && totalRecords) { const contentUpdatesBody = getContentUpdatesBody({ bulkOperationId, contentUpdates, - totalRecords: bulkDetails.totalNumOfRecords, + totalRecords, }); setIsLoadingPreview(true); @@ -158,7 +161,7 @@ const BulkEditInAppPreviewModal = ({ setIsLoadingPreview(false); }); } - }, [contentUpdates, open]); + }, [contentUpdates, open, totalRecords]); return ( { enabled: false, }); - const { mutateAsync: bulkOperationStart, isLoading } = useMutation({ + const { mutateAsync: bulkOperationStart, isLoading, data: startData } = useMutation({ mutationFn: async ({ id, approach, @@ -74,5 +74,6 @@ export const useBulkOperationStart = (mutationOptions = {}) => { return { bulkOperationStart, isLoading, + startData }; }; From 69815e61d9e697fb56d3809b8b9cd41aaac2e5f5 Mon Sep 17 00:00:00 2001 From: vashjs Date: Tue, 30 Jan 2024 13:10:00 +0100 Subject: [PATCH 7/7] update tests --- src/hooks/useBulkOperationStats.test.js | 79 +++++++++++++++++++++++++ src/hooks/usePagination.test.js | 40 +++++++++++++ 2 files changed, 119 insertions(+) create mode 100644 src/hooks/useBulkOperationStats.test.js create mode 100644 src/hooks/usePagination.test.js diff --git a/src/hooks/useBulkOperationStats.test.js b/src/hooks/useBulkOperationStats.test.js new file mode 100644 index 00000000..4190ab45 --- /dev/null +++ b/src/hooks/useBulkOperationStats.test.js @@ -0,0 +1,79 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { EDITING_STEPS } from '../constants'; +import { useBulkOperationStats } from './useBulkOperationStats'; + +jest.mock('react', () => { + const ActualReact = jest.requireActual('react'); + return { + ...ActualReact, + useContext: () => ({ + countOfRecords: 0, + setCountOfRecords: jest.fn(), + visibleColumns: [], + }), + }; +}); + +describe('useBulkOperationStats', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('initial state is set correctly', () => { + const { result } = renderHook(() => useBulkOperationStats({ bulkDetails: { matchedNumOfRecords: 5, totalNumOfRecords: 10 }, step: EDITING_STEPS.UPLOAD })); + + expect(result.current.countOfRecords).toBe(0); + expect(result.current.countOfErrors).toBe(undefined); + expect(result.current.totalCount).toBe(10); + expect(result.current.visibleColumns).toEqual([]); + }); + + test('state is updated correctly for UPLOAD step', () => { + const { result, rerender } = renderHook( + ({ bulkDetails, step }) => useBulkOperationStats({ bulkDetails, step }), + { + initialProps: { + bulkDetails: { matchedNumOfRecords: 5, matchedNumOfErrors: 2, totalNumOfRecords: 10 }, + step: EDITING_STEPS.UPLOAD, + }, + } + ); + + expect(result.current.countOfRecords).toBe(0); + expect(result.current.countOfErrors).toBe(2); + expect(result.current.totalCount).toBe(10); + + rerender({ + bulkDetails: { matchedNumOfRecords: 8, matchedNumOfErrors: 3, totalNumOfRecords: 15 }, + step: EDITING_STEPS.UPLOAD, + }); + + expect(result.current.countOfRecords).toBe(0); + expect(result.current.countOfErrors).toBe(3); + expect(result.current.totalCount).toBe(15); + }); + + test('state is updated correctly for COMMIT step', () => { + const { result, rerender } = renderHook( + ({ bulkDetails, step }) => useBulkOperationStats({ bulkDetails, step }), + { + initialProps: { + bulkDetails: { matchedNumOfRecords: 5, matchedNumOfErrors: 2, totalNumOfRecords: 10 }, + step: EDITING_STEPS.COMMIT, + }, + } + ); + + expect(result.current.countOfRecords).toBe(0); + expect(result.current.countOfErrors).toBe(undefined); + expect(result.current.totalCount).toBe(5); + + rerender({ + bulkDetails: { committedNumOfRecords: 8, committedNumOfErrors: 3, totalNumOfRecords: 15 }, + step: EDITING_STEPS.COMMIT, + }); + + expect(result.current.countOfRecords).toBe(0); + expect(result.current.countOfErrors).toBe(3); + }); +}); diff --git a/src/hooks/usePagination.test.js b/src/hooks/usePagination.test.js new file mode 100644 index 00000000..1f01676e --- /dev/null +++ b/src/hooks/usePagination.test.js @@ -0,0 +1,40 @@ +import { renderHook, act } from '@testing-library/react-hooks'; +import { usePagination } from './usePagination'; + +describe('usePagination', () => { + test('initial state is set correctly', () => { + const { result } = renderHook(() => usePagination({ limit: 10, offset: 0 })); + + expect(result.current.pagination).toEqual({ limit: 10, offset: 0 }); + }); + + test('changePage updates pagination correctly', () => { + const { result } = renderHook(() => usePagination({ limit: 10, offset: 0 })); + + act(() => { + result.current.changePage({ offset: 10 }); + }); + + expect(result.current.pagination).toEqual({ limit: 10, offset: 10 }); + }); + + test('changePage does not modify other properties', () => { + const { result } = renderHook(() => usePagination({ limit: 10, offset: 0 })); + + act(() => { + result.current.changePage({ offset: 10 }); + }); + + expect(result.current.pagination.limit).toBe(10); + }); + + test('changePage works with multiple properties', () => { + const { result } = renderHook(() => usePagination({ limit: 10, offset: 0 })); + + act(() => { + result.current.changePage({ offset: 10, limit: 20 }); + }); + + expect(result.current.pagination).toEqual({ limit: 20, offset: 10 }); + }); +});