From 2344051e138c874ba11fd374e4214585ca1a1049 Mon Sep 17 00:00:00 2001
From: FOLIO Translations Bot
<38661258+folio-translations@users.noreply.github.com>
Date: Tue, 23 Jan 2024 21:32:56 -0500
Subject: [PATCH 1/3] Lokalise: updates
---
translations/ui-users/cs_CZ.json | 26 +++++++++++++-------------
translations/ui-users/pl.json | 18 +++++++++---------
2 files changed, 22 insertions(+), 22 deletions(-)
diff --git a/translations/ui-users/cs_CZ.json b/translations/ui-users/cs_CZ.json
index 25a9e3703..e87d0bc03 100644
--- a/translations/ui-users/cs_CZ.json
+++ b/translations/ui-users/cs_CZ.json
@@ -565,13 +565,13 @@
"filters.status.active": "Aktivní",
"filters.status.inactive": "Neaktivní",
"data.loanActionMap.claimedReturned": "Údajně vráceno",
- "loans.claimReturned": "Prohlásit za vrácené",
+ "loans.claimReturned": "Prohlášeno za vrácené",
"loans.claimReturnedDialogBody": "{title} ({materialType}) (Čárový kód: {barcode}) bude označena jako údajně vrácená. {countIndex, plural, =-1 {Pro tuto jednotku mohou existovat otevřené žádanky.} =0 {} one {Pro tuto jednotku existuje {openRequestValue} žádanka.} other {Pro tuto jednotku existuje {openRequestValue} žádanek.}}",
- "loans.confirmClaimReturned": "Potvrdit vrácení reklamace",
+ "loans.confirmClaimReturned": "Potvrdit údajné vrácení",
"renewInProgress": "Probíhá prodloužení",
"item.callNumberComponents.prefix": "Prefix signatury",
"item.callNumberComponents.callNumber": "Signatura",
- "item.callNumberComponents.suffix": "Přípona signatury",
+ "item.callNumberComponents.suffix": "Suffix signatury",
"item.enumeration": "Výčet",
"item.volume": "Svazek",
"loans.details.effectiveCallNumber": "Efektivní signatura",
@@ -632,16 +632,16 @@
"data.loanActionMap.checkedInReturnedByPatron": "Vráceno (vráceno čtenářem)",
"data.loanActionMap.checkedInFoundByLibrary": "Vráceno (nalezeno knihovnou)",
"bulkClaimReturned.item.title": "Název",
- "bulkClaimReturned.preConfirm": "Potvrdit vrácení reklamace",
- "bulkClaimReturned.postConfirm": "Potvrzení vrácení reklamace",
- "bulkClaimReturned.summary": "{numLoans} jednotka(y) bude(ou) reklamována(y) jako vrácená(é).",
- "bulkClaimReturned.status": "Stav vrácení reklamace",
- "bulkClaimReturned.status.ok": "Jednotka byla úspěšně reklamována jako vrácená",
+ "bulkClaimReturned.preConfirm": "Potvrdit údajné vrácení",
+ "bulkClaimReturned.postConfirm": "Potvrzení údajného vrácení",
+ "bulkClaimReturned.summary": "{numLoans} jednotka(y) bude(ou) vrácená(é).",
+ "bulkClaimReturned.status": "Stav údajného vrácení",
+ "bulkClaimReturned.status.ok": "Jednotka byla úspěšně vrácená",
"bulkClaimReturned.status.notOk": "Jednotku nebylo možné reklamovat jako vrácenou: jednotka již byla údajně vrácena",
- "bulkClaimReturned.items.ok": "{numItems} jednotka(y) úspěšně reklamována(y) jako vrácená(é).",
- "bulkClaimReturned.items.notOk": "{numItems} jednotka(y) nebyla(y) reklamována(y) jako vrácená(é).",
+ "bulkClaimReturned.items.ok": "{numItems} jednotka(y) úspěšně je/jsou vrácena(é).",
+ "bulkClaimReturned.items.notOk": "{numItems} jednotka(y) nebyla(y) údajně vrácena(é).",
"bulkClaimReturned.moreInfoPlaceholder": "Zadat další informace o nároku (povinné)",
- "loans.resolveClaim": "Vyřešit reklamaci",
+ "loans.resolveClaim": "Vyřešit nárok",
"blocks.columns.automated.type": "Automatizované (nelze upravovat)",
"permission.reset.password": "Uživatelé: Vytvoření/resetování hesla",
"errors.proxies.duplicateUserMessage": "Byla zadán duplicitní zástupce.",
@@ -706,7 +706,7 @@
"reports.item.barcode": "Čárový kód",
"reports.item.callNumberComponents.prefix": "Prefix signatury",
"reports.item.callNumberComponents.callNumber": "Signatura",
- "reports.item.callNumberComponents.suffix": "Přípona signatury",
+ "reports.item.callNumberComponents.suffix": "Suffix signatury",
"reports.item.enumeration": "Výčet",
"reports.item.chronology": "Chronologie",
"reports.item.copyNumber": "Číslo kopie",
@@ -1139,5 +1139,5 @@
"information.userType.system": "Systém",
"userType": "Typ uživatele",
"data.loanActionMap.reminderFee": "Poplatek za připomenutí",
- "permission.profilepictures.view": "Users: Can view profile pictures"
+ "permission.profilepictures.view": "Uživatelé: Mohou zobrazit profilové obrázky"
}
\ No newline at end of file
diff --git a/translations/ui-users/pl.json b/translations/ui-users/pl.json
index 38dfa9e5d..08122df08 100644
--- a/translations/ui-users/pl.json
+++ b/translations/ui-users/pl.json
@@ -33,7 +33,7 @@
"loans.details.location": "Lokalizacja",
"loans.details.requestQueue": "Kolejka zamówień",
"loans.details.claimedReturned": "Zażądano zwrotu",
- "loans.details.loanPolicy": "Polityka wypożyczania",
+ "loans.details.loanPolicy": "Reguła wypożyczania",
"loans.details.lost": "Zagubiony",
"loans.details.borrower": "Wypożyczający",
"loans.details.renewalCount": "Liczba prolongat",
@@ -318,8 +318,8 @@
"requests.createRequest": "Utwórz zamówienie",
"requests.numOpenRequests": "{count, number} {count, plural, zero {otwartych zamówień} one {otwarte zamówienie} other {otwarte zamówienia}}",
"requests.numClosedRequests": "{count, number} {count, plural, one {zamknięte zamówienie} other {zamknięte zamówienia}}",
- "expandAll": "Rozwiń wszystkie",
- "collapseAll": "Zwiń wszystkie",
+ "expandAll": "Rozwiń wszystko",
+ "collapseAll": "Zwiń wszystko",
"loans.details.checkinServicePoint": "Punkt obsługi zwrotu",
"settings.label": "Użytkownicy",
"settings.general": "Ogólne",
@@ -526,7 +526,7 @@
"permissions.modal.save": "Zapisz",
"permissions.modal.cancel": "Anuluj",
"permissions.modal.search.header": "Wyszukaj i filtruj",
- "permissions.modal.search.resetAll": "Zresetuj wszystkie",
+ "permissions.modal.search.resetAll": "Resetuj wszystko",
"permissions.modal.total": "Wybrano łącznie: {count}",
"permissions.modal.hideSearchPane": "Ukryj okienko wyszukiwania i filtrów.",
"permissions.modal.showSearchPane": "Pokaż okienko wyszukiwania i filtrów.",
@@ -574,7 +574,7 @@
"item.callNumberComponents.suffix": "Sufiks sygnatury",
"item.enumeration": "Numeracja",
"item.volume": "Wolumin",
- "loans.details.effectiveCallNumber": "Ciąg znaków efektywnej sygnatury",
+ "loans.details.effectiveCallNumber": "Sygnatura",
"settings.customFields": "Pola niestandardowe",
"details.field.instance.type": "Instancja (typ materiału)",
"accounts.history.columns.instance": "Instancja (typ materiału)",
@@ -698,7 +698,7 @@
"reports.dueDate": "Termin zwrotu",
"reports.loanDate": "Data wypożyczenia",
"reports.loanPolicyId": "ID polityki wypożyczania",
- "reports.loanPolicy.name": "Polityka wypożyczania",
+ "reports.loanPolicy.name": "Reguła wypożyczania",
"reports.loanId": "ID wypożyczenia",
"reports.item.title": "Tytuł egzemplarza",
"reports.item.materialType.name": "Typ materiału",
@@ -949,7 +949,7 @@
"reports.financial.loanDate": "Loan date/time",
"reports.financial.dueDate": "Due date/time",
"reports.financial.returnDate": "Return date/time",
- "reports.financial.loanPolicy": "Loan policy",
+ "reports.financial.loanPolicy": "Reguła wypożyczania",
"reports.financial.overduePolicy": "Overdue policy",
"reports.financial.lostItemPolicy": "Lost item policy",
"reports.financial.loanDetails": "Loan details",
@@ -1048,7 +1048,7 @@
"lostItems.list.columnName.action.actualCostDetails": "Actual cost details",
"lostItems.modal.summaryMessageCharged": "A fee/fine of {actualCostToBill} was charged to {patronName} for {instanceTitle} ({materialType}).",
"lostItems.modal.summaryMessageNotCharged": "A fee/fine was not charged to {patronName} for {instanceTitle} ({materialType}).",
- "lostItems.modal.button.close": "Close",
+ "lostItems.modal.button.close": "Zamknij",
"affiliations.manager.filter.assignment": "Affiliation assignment status",
"affiliations.manager.filter.assignment.assigned": "Assigned",
"affiliations.manager.filter.assignment.unassigned": "Unassigned",
@@ -1139,5 +1139,5 @@
"information.userType.system": "System",
"userType": "User type",
"data.loanActionMap.reminderFee": "Reminder fee",
- "permission.profilepictures.view": "Users: Can view profile pictures"
+ "permission.profilepictures.view": "Użytkownicy: Mogą zobaczyć zdjęcia profilowe"
}
\ No newline at end of file
From 8fa0ca0324795db10a345cec5d7fabb9ae335149 Mon Sep 17 00:00:00 2001
From: Priyanka Terala <104053200+Terala-Priyanka@users.noreply.github.com>
Date: Wed, 24 Jan 2024 18:38:27 +0530
Subject: [PATCH 2/3] UIU-3038 - Update sub permissions of permission 'Users:
Can view user profiles' (#2616)
---
CHANGELOG.md | 1 +
package.json | 3 ++-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c2c2e7cf4..7637bb2cd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
* Create new permission 'Users: Can view profile pictures'. Refs UIU-3018.
* Format currency values as currencies, not numbers. Refs UIU-2026.
* Show country name in user address instead of country id. Refs UIU-2976.
+* Update sub permissions of permission 'Users: Can view user profiles'. Refs UIU-3038.
## [10.0.4](https://github.com/folio-org/ui-users/tree/v10.0.4) (2023-11-10)
[Full Changelog](https://github.com/folio-org/ui-users/compare/v10.0.3...v10.0.4)
diff --git a/package.json b/package.json
index 19f061521..b61463770 100644
--- a/package.json
+++ b/package.json
@@ -985,7 +985,8 @@
"description": "Also includes basic permissions to view users",
"subPermissions": [
"ui-users.view",
- "users.profile-picture.item.get"
+ "users.profile-picture.item.get",
+ "users.configurations.item.get"
],
"visible": true
},
From 9bf823281fc60354bec9ccc8a7cab935d8aba056 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C5=82=20Kuklis?=
Date: Wed, 24 Jan 2024 09:31:39 -0500
Subject: [PATCH 3/3] UIU-3029: Add patron notice print jobs to action menu
(#2614)
* Add PatronNoticePrintJobs
* Add tests
* Cleanup
* Add permissions
* Add more tests
* Add more tests
---
CHANGELOG.md | 1 +
package.json | 24 ++-
.../PatronNoticePrintJobsLink.js | 35 ++++
.../PatronNoticePrintJobsLink/index.js | 1 +
src/index.js | 1 +
src/routes/PatronNoticePrintJobsContainer.js | 68 ++++++++
.../PatronNoticePrintJobsContainer.test.js | 69 ++++++++
src/routes/index.js | 1 +
.../PatronNoticePrintJobs.css | 0
.../PatronNoticePrintJobs.js | 150 ++++++++++++++++++
.../PatronNoticePrintJobs.test.js | 87 ++++++++++
src/views/PatronNoticePrintJobs/index.js | 1 +
src/views/UserSearch/UserSearch.js | 2 +
test/jest/helpers/renderWithRouter.js | 5 +-
translations/ui-users/en.json | 13 +-
15 files changed, 454 insertions(+), 4 deletions(-)
create mode 100644 src/components/PatronNoticePrintJobsLink/PatronNoticePrintJobsLink.js
create mode 100644 src/components/PatronNoticePrintJobsLink/index.js
create mode 100644 src/routes/PatronNoticePrintJobsContainer.js
create mode 100644 src/routes/PatronNoticePrintJobsContainer.test.js
create mode 100644 src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.css
create mode 100644 src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.js
create mode 100644 src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.test.js
create mode 100644 src/views/PatronNoticePrintJobs/index.js
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7637bb2cd..14fbe8e7a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,7 @@
* Create new permission 'Users: Can view profile pictures'. Refs UIU-3018.
* Format currency values as currencies, not numbers. Refs UIU-2026.
* Show country name in user address instead of country id. Refs UIU-2976.
+* Add patron notice print jobs to action menu. Refs UIU-3029.
* Update sub permissions of permission 'Users: Can view user profiles'. Refs UIU-3038.
## [10.0.4](https://github.com/folio-org/ui-users/tree/v10.0.4) (2023-11-10)
diff --git a/package.json b/package.json
index b61463770..f9696f54f 100644
--- a/package.json
+++ b/package.json
@@ -46,7 +46,9 @@
"loan-policy-storage": "1.0 2.0",
"loan-storage": "4.0 5.0 6.0 7.0",
"notes": "2.0 3.0",
- "request-storage": "2.5 3.0 4.0 5.0 6.0"
+ "request-storage": "2.5 3.0 4.0 5.0 6.0",
+ "batch-print": "1.0"
+
},
"permissionSets": [
{
@@ -1023,6 +1025,26 @@
"ui-users.settings.patron-blocks.view"
],
"visible": true
+ },
+ {
+ "permissionName": "ui-users.view-patron-notice-print-jobs",
+ "displayName": "Users: View patron notice print jobs",
+ "subPermissions": [
+ "ui-users.view",
+ "mod-batch-print.entries.collection.get",
+ "mod-batch-print.entries.item.get",
+ "mod-batch-print.print.read"
+ ],
+ "visible": true
+ },
+ {
+ "permissionName": "ui-users.remove-patron-notice-print-jobs",
+ "displayName": "Users: View and remove patron notice print jobs",
+ "subPermissions": [
+ "ui-circulation.settings.view-patron-notice-print-jobs",
+ "mod-batch-print.entries.item.delete"
+ ],
+ "visible": true
}
]
},
diff --git a/src/components/PatronNoticePrintJobsLink/PatronNoticePrintJobsLink.js b/src/components/PatronNoticePrintJobsLink/PatronNoticePrintJobsLink.js
new file mode 100644
index 000000000..54489c633
--- /dev/null
+++ b/src/components/PatronNoticePrintJobsLink/PatronNoticePrintJobsLink.js
@@ -0,0 +1,35 @@
+import { FormattedMessage } from 'react-intl';
+import { useHistory } from 'react-router-dom';
+
+import { IfPermission } from '@folio/stripes/core';
+import {
+ Button,
+ Icon,
+} from '@folio/stripes/components';
+
+const PatronNoticePrintJobsLink = () => {
+ const history = useHistory();
+
+ return (
+
+
+
+ );
+};
+
+
+export default PatronNoticePrintJobsLink;
diff --git a/src/components/PatronNoticePrintJobsLink/index.js b/src/components/PatronNoticePrintJobsLink/index.js
new file mode 100644
index 000000000..64e420610
--- /dev/null
+++ b/src/components/PatronNoticePrintJobsLink/index.js
@@ -0,0 +1 @@
+export { default } from './PatronNoticePrintJobsLink';
diff --git a/src/index.js b/src/index.js
index c6beac6f0..800ea1b37 100644
--- a/src/index.js
+++ b/src/index.js
@@ -215,6 +215,7 @@ class UsersRouting extends React.Component {
+
diff --git a/src/routes/PatronNoticePrintJobsContainer.js b/src/routes/PatronNoticePrintJobsContainer.js
new file mode 100644
index 000000000..dd9250af6
--- /dev/null
+++ b/src/routes/PatronNoticePrintJobsContainer.js
@@ -0,0 +1,68 @@
+
+import PropTypes from 'prop-types';
+import { useHistory } from 'react-router-dom';
+
+import { stripesConnect } from '@folio/stripes/core';
+
+import PatronNoticePrintJobs from '../views/PatronNoticePrintJobs';
+
+const PatronNoticePrintJobsContainer = (props) => {
+ const { mutator, resources } = props;
+ const records = resources?.entries?.records;
+ const history = useHistory();
+
+ const onClose = () => {
+ const { location } = props;
+
+ if (location.state) {
+ history.goBack();
+ } else {
+ history.push('/users?sort=name');
+ }
+ };
+
+ return (
+
+ );
+};
+
+PatronNoticePrintJobsContainer.manifest = {
+ entries: {
+ type: 'okapi',
+ path: 'print/entries',
+ params: {
+ query: 'type="BATCH"',
+ sortby: 'created/sort.descending'
+ },
+ records: 'items',
+ throwErrors: false,
+ },
+ printingJob: {
+ type: 'okapi',
+ path: 'print/entries',
+ accumulate: 'true',
+ fetch: false,
+ throwErrors: false,
+ },
+};
+
+PatronNoticePrintJobsContainer.propTypes = {
+ resources: PropTypes.shape({
+ entries: PropTypes.shape({
+ records: PropTypes.arrayOf(PropTypes.object),
+ }),
+ }).isRequired,
+ mutator: PropTypes.shape({
+ printingJob: PropTypes.shape({
+ GET: PropTypes.func,
+ reset: PropTypes.func,
+ }),
+ }).isRequired,
+ location: PropTypes.object,
+};
+
+export default stripesConnect(PatronNoticePrintJobsContainer);
diff --git a/src/routes/PatronNoticePrintJobsContainer.test.js b/src/routes/PatronNoticePrintJobsContainer.test.js
new file mode 100644
index 000000000..e2bd284e9
--- /dev/null
+++ b/src/routes/PatronNoticePrintJobsContainer.test.js
@@ -0,0 +1,69 @@
+import { waitFor, fireEvent } from '@folio/jest-config-stripes/testing-library/react';
+
+import renderWithRouter from 'helpers/renderWithRouter';
+import PatronNoticePrintJobsContainer from './PatronNoticePrintJobsContainer';
+
+
+jest.mock('../views/PatronNoticePrintJobs', () => {
+ return jest.fn(({ onClose }) => ());
+});
+
+jest.mock('history', () => {
+ return {
+ createMemoryHistory: jest.fn(() => ({
+ push: jest.fn(),
+ location: {},
+ listen: jest.fn(),
+ goBack: jest.fn(),
+ })),
+ };
+});
+
+const mockMutator = {
+ printingJob: {
+ GET: jest.fn(),
+ reset: jest.fn(),
+ },
+};
+
+const mockResources = {
+ entries: {
+ records: [],
+ },
+};
+
+const renderPatronNoticePrintJobsContainer = (extraProps) => renderWithRouter(
+
+);
+
+describe('PatronNoticePrintJobsContainer', () => {
+ it('should render PatronNoticePrintJobs', () => {
+ const { container } = renderPatronNoticePrintJobsContainer();
+ expect(container).toBeInTheDocument();
+ });
+
+ it('should go back if location state exists', async () => {
+ const { getByTestId, history } = renderPatronNoticePrintJobsContainer({
+ location: { state: { from: '/previous-page' } }
+ });
+
+ fireEvent.click(getByTestId('close-button'));
+
+ await waitFor(() => {
+ expect(history.goBack).toHaveBeenCalled();
+ });
+ });
+
+
+ it('should redirect to /users?sort=name if location state does not exist', async () => {
+ const { getByTestId, history } = renderPatronNoticePrintJobsContainer({
+ location: {}
+ });
+
+ fireEvent.click(getByTestId('close-button'));
+
+ await waitFor(() => {
+ expect(history.push).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/routes/index.js b/src/routes/index.js
index 52d8e2644..9d4965702 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -9,3 +9,4 @@ export { default as LoansListingContainer } from './LoansListingContainer';
export { default as LoanDetailContainer } from './LoanDetailContainer';
export { default as AccountDetailsContainer } from './AccountDetailsContainer';
export { default as LostItemsContainer } from './LostItemsContainer';
+export { default as PatronNoticePrintJobsContainer } from './PatronNoticePrintJobsContainer';
diff --git a/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.css b/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.css
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.js b/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.js
new file mode 100644
index 000000000..996ea0142
--- /dev/null
+++ b/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.js
@@ -0,0 +1,150 @@
+
+import { orderBy } from 'lodash';
+import { Button, Pane, MenuSection, MultiColumnList, Checkbox, FormattedDate, FormattedTime, TextLink } from '@folio/stripes/components';
+import { useEffect, useState } from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+import { useOkapiKy, useCallout, useStripes } from '@folio/stripes/core';
+
+import css from './PatronNoticePrintJobs.css';
+
+const ASC = 'ascending';
+const DESC = 'descending';
+const visibleColumns = ['id', 'created'];
+
+export const generateFormatter = (markPrintJobForDeletion, openPDF) => {
+ return {
+ id: (item) => (
+ markPrintJobForDeletion(item)}
+ />
+ ),
+ created: (item) => (
+ openPDF(item)}>
+
+
+ )
+ };
+};
+
+const PatronNoticePrintJobs = (props) => {
+ const { records, mutator, onClose } = props;
+ const [contentData, setContentData] = useState([]);
+ const [sortOrder, setSortOrder] = useState(DESC);
+ const [allSelected, toggleSelectAll] = useState(false);
+ const sort = () => setSortOrder(sortOrder === DESC ? ASC : DESC);
+ const ky = useOkapiKy();
+ const callout = useCallout();
+ const stripes = useStripes();
+
+ const markPrintJobForDeletion = (item) => {
+ const clonedData = [...contentData];
+ const index = clonedData.findIndex(el => el.id === item.id);
+
+ clonedData[index] = { ...item, selected: !item.selected };
+ setContentData(clonedData);
+ };
+
+ const markAllPrintJobForDeletions = () => {
+ toggleSelectAll(!allSelected);
+ const clonedData = contentData.map(el => ({ ...el, selected: !allSelected }));
+ setContentData(clonedData);
+ };
+
+ const openPDF = async (item) => {
+ try {
+ mutator.printingJob.reset();
+ const { content } = await mutator.printingJob.GET({ path: `print/entries/${item.id}` });
+ const bytes = new Uint8Array(content.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
+ const blob = new Blob([bytes], { type: 'application/pdf' });
+ const url = URL.createObjectURL(blob);
+
+ window.open(url, '_blank');
+ } catch (error) {
+ callout.sendCallout({
+ message: ,
+ type: 'error',
+ });
+ }
+ };
+
+ useEffect(() => {
+ const updatedRecords =
+ orderBy(records, (item) => item.created, sortOrder === DESC ? 'desc' : 'asc')
+ .map(record => ({
+ ...record,
+ selected: !!record.selected,
+ }));
+
+ setContentData(updatedRecords);
+ }, [records, sortOrder]);
+
+ const formatter = generateFormatter(markPrintJobForDeletion, openPDF);
+ const columnMapping = {
+ id: markAllPrintJobForDeletions()} />,
+ created: ,
+ };
+
+ const renderActionMenu = ({ onToggle }) => {
+ const removeSelectedPrintJobs = async () => {
+ const selectedJobs = contentData.filter(item => item.selected);
+ const ids = selectedJobs.map(job => job.id).join(',');
+ const filtered = contentData.filter(item => !item.selected);
+
+ await ky.delete(`print/entries?ids=${ids}`);
+
+ setContentData(filtered);
+ onToggle();
+ toggleSelectAll(false);
+ };
+
+ return (
+ }>
+
+
+ );
+ };
+
+ const actionMenu = stripes?.hasPerm('ui-users.remove-patron-notice-print-jobs') ? renderActionMenu : null;
+
+ return (
+
+ }
+ defaultWidth="fill"
+ dismissible
+ actionMenu={actionMenu}
+ onClose={onClose}
+ >
+
+
+ );
+};
+
+PatronNoticePrintJobs.propTypes = {
+ records: PropTypes.arrayOf(PropTypes.object),
+ onClose: PropTypes.func,
+ mutator: PropTypes.shape({
+ printingJob: PropTypes.shape({
+ GET: PropTypes.func,
+ reset: PropTypes.func,
+ }),
+ }).isRequired,
+};
+
+export default PatronNoticePrintJobs;
diff --git a/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.test.js b/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.test.js
new file mode 100644
index 000000000..da95fdd95
--- /dev/null
+++ b/src/views/PatronNoticePrintJobs/PatronNoticePrintJobs.test.js
@@ -0,0 +1,87 @@
+
+import { render, waitFor, screen } from '@folio/jest-config-stripes/testing-library/react';
+import userEvent from '@folio/jest-config-stripes/testing-library/user-event';
+import PatronNoticePrintJobs, { generateFormatter } from './PatronNoticePrintJobs';
+
+jest.unmock('@folio/stripes/components');
+
+global.URL.createObjectURL = jest.fn();
+global.window.open = jest.fn();
+
+const PDF_IN_HEX = '255044462d312e330a25e2e3cfd30a312030206f626a0a3c3c2f57696474682032203020522f4865696768742033203020522f547970652033203020522f537562747970652034203020522f46696c7465722035203020522f436f6c6f7253706163652036203020522f4c656e6774682037203020522f4865696768742038203020522f417373656d626c792035203020522f50726f632033203020';
+
+describe('PatronNoticePrintJobs', () => {
+ afterAll(() => {
+ jest.restoreAllMocks();
+ });
+
+ const mockMutator = {
+ printingJob: {
+ GET: jest.fn(() => ({ content: PDF_IN_HEX })),
+ reset: jest.fn(),
+ },
+ entries: {
+ reset: jest.fn(),
+ }
+ };
+
+ const mockRecords = [
+ { id: '1', created: '2022-01-01T12:00:00Z', selected: false },
+ { id: '2', created: '2022-01-02T12:00:00Z', selected: false },
+ ];
+
+ it('renders data', async () => {
+ const { container } = render();
+ expect(container).toHaveTextContent('2022-01-01');
+ });
+
+ it('calls openPDF', async () => {
+ render();
+
+ const textElement = screen.getByText(/2022-01-01/i);
+ const printJobLinkElement = textElement.closest('.printJobLink');
+
+ userEvent.click(printJobLinkElement);
+
+ await waitFor(() => {
+ expect(mockMutator.printingJob.GET).toHaveBeenCalledTimes(1);
+ });
+ });
+
+
+ it('calls markAllPrintJobForDeletions', async () => {
+ const { getAllByRole } = render();
+ const checkboxes = getAllByRole('checkbox');
+
+ userEvent.click(checkboxes[0]);
+
+ await waitFor(() => {
+ checkboxes.slice(1).forEach((checkbox) => {
+ expect(checkbox).toBeChecked();
+ });
+ });
+ });
+
+ it('calls markPrintJobForDeletion', async () => {
+ const { getAllByRole } = render();
+ const checkboxes = getAllByRole('checkbox');
+ const checkbox = checkboxes[1];
+
+ userEvent.click(checkbox);
+
+ await waitFor(() => {
+ expect(checkbox).toBeChecked();
+ });
+ });
+});
+
+describe('generateFormatter', () => {
+ it('returns correct formatter', () => {
+ const markPrintJobForDeletion = jest.fn();
+ const openPDF = jest.fn();
+ const formatter = generateFormatter(markPrintJobForDeletion, openPDF);
+
+ expect(typeof formatter.id).toBe('function');
+ expect(typeof formatter.created).toBe('function');
+ });
+});
diff --git a/src/views/PatronNoticePrintJobs/index.js b/src/views/PatronNoticePrintJobs/index.js
new file mode 100644
index 000000000..8578b7841
--- /dev/null
+++ b/src/views/PatronNoticePrintJobs/index.js
@@ -0,0 +1 @@
+export { default } from './PatronNoticePrintJobs';
diff --git a/src/views/UserSearch/UserSearch.js b/src/views/UserSearch/UserSearch.js
index b28287d40..6d1648761 100644
--- a/src/views/UserSearch/UserSearch.js
+++ b/src/views/UserSearch/UserSearch.js
@@ -45,6 +45,7 @@ import CashDrawerReconciliationReportPDF from '../../components/data/reports/cas
import CashDrawerReconciliationReportCSV from '../../components/data/reports/cashDrawerReconciliationReportCSV';
import FinancialTransactionsReport from '../../components/data/reports/FinancialTransactionsReport';
import LostItemsLink from '../../components/LostItemsLink';
+import PatronNoticePrintJobsLink from '../../components/PatronNoticePrintJobsLink';
import Filters from './Filters';
import css from './UserSearch.css';
@@ -292,6 +293,7 @@ class UserSearch extends React.Component {
+
{
+ const history = createMemoryHistory();
const renderFn = options.rerender ? rtlApi.rerender : render;
rtlApi = renderFn(
@@ -22,7 +23,7 @@ const renderWithRouter = (children, options = {}) => {
);
- return rtlApi;
+ return { ...rtlApi, history };
};
export default renderWithRouter;
diff --git a/translations/ui-users/en.json b/translations/ui-users/en.json
index 303194116..5c89f7c7a 100644
--- a/translations/ui-users/en.json
+++ b/translations/ui-users/en.json
@@ -66,6 +66,7 @@
"user.unknown": "Unknown user",
"action": "Action",
"actionMenu.lostItems": "Lost items requiring actual cost",
+ "actionMenu.patronNoticePrintJobs": "View patron notice print jobs (PDF)",
"dueDate": "Due date",
"loanDate": "Loan date",
"returnDate": "Return date",
@@ -1066,6 +1067,8 @@
"permission.lost-items.requiring-actual-cost":"Users: Can process lost items requiring actual cost",
"permission.delete":"Users: Can delete user profile if user does not have any open transactions",
"permission.opentransactions":"Users: Can check open transactions",
+ "permission.view-patron-notice-print-jobs": "Users: View patron notice print jobs",
+ "permission.remove-patron-notice-print-jobs": "Users: View and remove patron notice print jobs",
"bulkClaimReturned.item.title": "Title",
"bulkClaimReturned.preConfirm": "Confirm claim returned",
"bulkClaimReturned.postConfirm": "Claim returned confirmation",
@@ -1140,5 +1143,13 @@
"lostItems.notification.billed": "A fee/fine of {amount} has been successfully charged to {patronName}",
"lostItems.notification.billedBefore": "{patronName} has already been billed for this item by another user",
"lostItems.notification.cancelled": "A lost item fee will not be charged to {patronName}",
- "lostItems.notification.serverError": "A system error has occurred - please try again later or contact your FOLIO support staff"
+ "lostItems.notification.serverError": "A system error has occurred - please try again later or contact your FOLIO support staff",
+ "patronNoticePrintJobs.label": "Patron notice print jobs",
+ "patronNoticePrintJobs.actions": "Actions",
+ "patronNoticePrintJobs.actions.delete": "Delete selected print jobs",
+ "patronNoticePrintJobs.name": "Name",
+ "patronNoticePrintJobs.email": "Email",
+ "patronNoticePrintJobs.updated": "Updated",
+ "patronNoticePrintJobs.created": "Created",
+ "patronNoticePrintJobs.errors.pdf": "'PDF generation failed"
}