Skip to content

Commit

Permalink
First attempt at full-set-of-failed-records CSV export (#86)
Browse files Browse the repository at this point in the history
* Move record-export code out into own function
* Provide all records in the result set, not just the currently visible page
* This is done using the resource's GET mutator to step through pages
* Use mutator.reset after fecthing records

Fixes UIHAADM-140, sort of.

The problem is that using the stripes-connect GET mutator to iterate through the records somehow affects the state — probably something deep inside the Redux store — in a way that leaves the UI trying to display records that are off the end of the list.

I have mitigated this using `mutator.reset()`, which leaves the page blank and looking like it's loading. It's not ideal, but it's not actively wrong. I guess.
  • Loading branch information
MikeTaylor authored Dec 20, 2024
1 parent aec6f83 commit 8f8e84c
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Explicit paging for list of failed records. Fixes UIHAADM-141.
* Explicit paging for list of harvestables. Fixes UIHAADM-142.
* When downloading Failed Records, provide all records in the result set, not just the currently visible page. Fixes UIHAADM-140.

## [2.2.0](https://github.com/folio-org/ui-harvester-admin/tree/v2.2.0) (2024-10-23)

Expand Down
4 changes: 4 additions & 0 deletions src/routes/RecordsRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const RecordsRoute = ({ stripes, resources, mutator, children }) => {
hasLoaded={hasLoaded}
error={error}
onNeedMoreData={handleNeedMoreData}
recordsMutator={mutator.records}
>
{children}
</Records>
Expand Down Expand Up @@ -102,6 +103,9 @@ RecordsRoute.propTypes = {
query: PropTypes.shape({
update: PropTypes.func.isRequired,
}).isRequired,
records: PropTypes.shape({
GET: PropTypes.func.isRequired,
}).isRequired,
}).isRequired,
children: PropTypes.object, // XXX may need to add .isRequired later
};
Expand Down
41 changes: 30 additions & 11 deletions src/views/Records/Records.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,36 @@ import ErrorMessage from '../../components/ErrorMessage';
import packageInfo from '../../../package';


function renderActionMenu(onToggle, intl, data, renderedColumnsMenu) {
function exportAllRecords(resultCount, recordsMutator) {
const RCI = 100; // Probably keep in sync with RESULT_COUNT_INCREMENT from RecordsRoute.js

const p = [];
for (let offset = 0; offset < resultCount; offset += RCI) {
p.push(recordsMutator.GET({ params: { offset, limit: RCI } }));
}

Promise.all(p).then(res => {
recordsMutator.reset();
const records = res.flat().filter(r => r !== undefined).map(r => ({
...r,
errors: errors2string(r.recordErrors),
originalRecord: undefined,
}));

exportToCsv(records, {});
});
}


function renderActionMenu(onToggle, intl, data, resultCount, recordsMutator, renderedColumnsMenu) {
return (
<div>
<MenuSection label={intl.formatMessage({ id: 'ui-harvester-admin.reports' })}>
<Button
aria-label={intl.formatMessage({ id: 'ui-harvester-admin.export-csv' })}
disabled={data.records.length === 0}
disabled={!resultCount}
buttonStyle="dropdownItem"
onClick={() => {
const expanded = data.records.map(r => ({
...r,
errors: errors2string(r.recordErrors),
}));
exportToCsv(expanded, {});
onToggle();
}}
onClick={() => { exportAllRecords(resultCount, recordsMutator); onToggle(); }}
>
<Icon icon="download">
<FormattedMessage id="ui-harvester-admin.export-csv" />
Expand All @@ -47,6 +61,7 @@ function Records({
error,
hasLoaded,
onNeedMoreData,
recordsMutator,
children,
}) {
const intl = useIntl();
Expand Down Expand Up @@ -108,7 +123,7 @@ function Records({
padContent={false}
paneTitle={paneTitle}
paneSub={<FormattedMessage id="ui-harvester-admin.resultCount" values={{ count: resultCount }} />}
actionMenu={({ onToggle }) => renderActionMenu(onToggle, intl, data, renderColumnsMenu)}
actionMenu={({ onToggle }) => renderActionMenu(onToggle, intl, data, resultCount, recordsMutator, renderColumnsMenu)}
>
<MultiColumnList
autosize
Expand Down Expand Up @@ -164,6 +179,10 @@ Records.propTypes = {
error: PropTypes.string,
hasLoaded: PropTypes.bool.isRequired,
onNeedMoreData: PropTypes.func.isRequired,
recordsMutator: PropTypes.shape({
GET: PropTypes.func.isRequired,
reset: PropTypes.func.isRequired,
}).isRequired,
children: PropTypes.oneOfType([
PropTypes.object.isRequired,
PropTypes.arrayOf(PropTypes.object.isRequired).isRequired,
Expand Down

0 comments on commit 8f8e84c

Please sign in to comment.