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" }