(
+
+
+
+ )}
+ />
diff --git a/src/routes/PatronPreRegistrationRecordsContainer.js b/src/routes/PatronPreRegistrationRecordsContainer.js
new file mode 100644
index 000000000..ebf268200
--- /dev/null
+++ b/src/routes/PatronPreRegistrationRecordsContainer.js
@@ -0,0 +1,96 @@
+import PropTypes from 'prop-types';
+import { useHistory } from 'react-router-dom';
+import { get } from 'lodash';
+
+import { stripesConnect } from '@folio/stripes/core';
+import {
+ makeQueryFunction,
+ StripesConnectedSource,
+} from '@folio/stripes/smart-components';
+import PatronsPreRegistrationListContainer from '../views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer';
+
+import { PATRON_PREREGISTRATION_RECORDS_NAME } from '../constants';
+
+const RESULT_COUNT_INCREMENT = 100;
+const PAGE_AMOUNT = 100;
+
+const PatronPreRegistrationRecordsContainer = ({
+ resources,
+ mutator,
+ stripes,
+}) => {
+ const history = useHistory();
+ const source = new StripesConnectedSource({ resources, mutator }, stripes.logger, PATRON_PREREGISTRATION_RECORDS_NAME);
+ const data = get(resources, `${PATRON_PREREGISTRATION_RECORDS_NAME}.records`, []);
+
+ const queryGetter = () => {
+ return get(resources, 'query', {});
+ };
+
+ const onNeedMoreData = (askAmount, index) => {
+ const { resultOffset } = mutator;
+
+ if (source) {
+ if (resultOffset && index >= 0) {
+ source.fetchOffset(index);
+ } else {
+ source.fetchMore(RESULT_COUNT_INCREMENT);
+ }
+ }
+ };
+
+ const onClose = () => {
+ history.push('/users?sort=name');
+ };
+
+ return (
+
+ );
+};
+
+PatronPreRegistrationRecordsContainer.manifest = {
+ query: { initialValue: {} },
+ resultCount: { initialValue: 0 },
+ resultOffset: { initialValue: 0 },
+ [PATRON_PREREGISTRATION_RECORDS_NAME]: {
+ type: 'okapi',
+ records: 'staging_users',
+ resultOffset: '%{resultOffset}',
+ resultDensity: 'sparse',
+ perRequest: PAGE_AMOUNT,
+ path: 'staging-users',
+ GET: {
+ params: {
+ query: makeQueryFunction(
+ 'cql.allRecords=1',
+ '(keywords="%{query.query}*") AND status == "TIER-2"',
+ {
+ 'firstName': 'personal.firstName',
+ 'lastName': 'personal.lastName',
+ 'middleName': 'personal.middleName',
+ 'preferredFirsName': 'personal.preferredFirstName',
+ 'email': 'personal.email',
+ },
+ '',
+ 2
+ ),
+ },
+ staticFallback: { params: {} },
+ },
+ }
+};
+
+PatronPreRegistrationRecordsContainer.propTypes = {
+ mutator: PropTypes.object,
+ stripes: PropTypes.object,
+ resources: PropTypes.object,
+};
+
+export default stripesConnect(PatronPreRegistrationRecordsContainer);
diff --git a/src/routes/PatronPreRegistrationRecordsContainer.test.js b/src/routes/PatronPreRegistrationRecordsContainer.test.js
new file mode 100644
index 000000000..581d40ee3
--- /dev/null
+++ b/src/routes/PatronPreRegistrationRecordsContainer.test.js
@@ -0,0 +1,118 @@
+import { render, screen, fireEvent } from '@folio/jest-config-stripes/testing-library/react';
+
+import preRegistrationRecords from 'fixtures/preRegistrationRecords';
+import buildStripes from '__mock__/stripes.mock';
+
+import { createMemoryHistory } from 'history';
+import { MemoryRouter, useHistory } from 'react-router-dom';
+
+import { StripesConnectedSource } from '@folio/stripes/smart-components';
+
+import PatronPreRegistrationRecordsContainer from './PatronPreRegistrationRecordsContainer';
+
+jest.unmock('@folio/stripes/components');
+jest.unmock('@folio/stripes/smart-components');
+jest.mock('@folio/stripes/components', () => ({
+ ...jest.requireActual('@folio/stripes/components'),
+ SearchField: jest.fn((props) => (
+
+ )),
+}));
+jest.mock('../views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer', () => {
+ return jest.fn(({ onClose, onNeedMoreData }) => (
+
+ Mocked component PatronsPreRegistrationListContainer
+
+
+
+ ));
+});
+jest.mock('react-router-dom', () => ({
+ ...jest.requireActual('react-router-dom'),
+ useHistory: jest.fn(),
+}));
+
+const history = createMemoryHistory();
+history.push = jest.fn();
+const props = {
+ resources: {
+ patronPreRegistrationRecords: {
+ records: preRegistrationRecords,
+ },
+ resultCount: 1,
+ resultOffset: 0,
+ },
+ mutator: {
+ patronPreRegistrationRecords:{
+ GET: jest.fn(),
+ },
+ resultOffset: 100
+ },
+ stripes: buildStripes(),
+ history,
+};
+
+const renderPatronPreRegistrationRecordsContainer = (alteredProps) => render(
+
+
+
+);
+
+describe('PatronPreRegistrationRecordsContainer', () => {
+ let mockHistory;
+ let mockSource;
+
+ beforeEach(() => {
+ // Set up the mock history object
+ mockHistory = { push: jest.fn() };
+ useHistory.mockReturnValue(mockHistory);
+
+ // Mock the StripesConnectedSource instance
+ mockSource = {
+ fetchOffset: jest.fn(),
+ fetchMore: jest.fn(),
+ };
+
+ jest.spyOn(StripesConnectedSource.prototype, 'fetchOffset').mockImplementation(mockSource.fetchOffset);
+ jest.spyOn(StripesConnectedSource.prototype, 'fetchMore').mockImplementation(mockSource.fetchMore);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render', () => {
+ renderPatronPreRegistrationRecordsContainer();
+ expect(screen.getByTestId('mock-PatronsPreRegistrationListContainer')).toBeInTheDocument();
+ });
+
+ it('should call onClose method', () => {
+ renderPatronPreRegistrationRecordsContainer();
+ fireEvent.click(screen.getByTestId('close-button'));
+
+ expect(mockHistory.push).toHaveBeenCalledWith('/users?sort=name');
+ });
+
+ it('should call onNeedMoreData method', () => {
+ renderPatronPreRegistrationRecordsContainer();
+ fireEvent.click(screen.getByTestId('need-more-button'));
+
+ expect(mockSource.fetchOffset).toHaveBeenCalledWith(1);
+ });
+
+ it('should call onNeedMoreData method', () => {
+ const alteredProps = {
+ ...props,
+ mutator: {
+ ...props.mutator,
+ resultOffset: 0
+ },
+ };
+ renderPatronPreRegistrationRecordsContainer(alteredProps);
+ fireEvent.click(screen.getByTestId('need-more-button'));
+
+ expect(mockSource.fetchMore).toHaveBeenCalledWith(100);
+ });
+});
diff --git a/src/routes/index.js b/src/routes/index.js
index 9d4965702..b36dfaa75 100644
--- a/src/routes/index.js
+++ b/src/routes/index.js
@@ -9,4 +9,5 @@ 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 PatronPreRegistrationRecordsContainer } from './PatronPreRegistrationRecordsContainer';
export { default as PatronNoticePrintJobsContainer } from './PatronNoticePrintJobsContainer';
diff --git a/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.js b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.js
new file mode 100644
index 000000000..f2d34a3d4
--- /dev/null
+++ b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.js
@@ -0,0 +1,75 @@
+import PropTypes from 'prop-types';
+import { MultiColumnList, Button } from '@folio/stripes/components';
+import { useIntl, FormattedMessage } from 'react-intl';
+import { get, noop } from 'lodash';
+
+import { visibleColumns, columnMapping, COLUMNS_NAME } from './constants';
+
+const PatronsPreRegistrationList = ({
+ data,
+ isEmptyMessage,
+ totalCount,
+ onNeedMoreData
+}) => {
+ const intl = useIntl();
+
+ const preRegistrationsListFormatter = () => ({
+ [COLUMNS_NAME.ACTION]: () => (
+
+ ),
+ [COLUMNS_NAME.FIRST_NAME]: user => get(user, ['generalInfo', 'firstName']),
+ [COLUMNS_NAME.LAST_NAME]: user => get(user, ['generalInfo', 'lastName']),
+ [COLUMNS_NAME.MIDDLE_NAME]: user => get(user, ['generalInfo', 'middleName']),
+ [COLUMNS_NAME.PREFERRED_FIRST_NAME]: user => get(user, ['generalInfo', 'preferredFirstName']),
+ [COLUMNS_NAME.EMAIL]: user => get(user, ['contactInfo', 'email']),
+ [COLUMNS_NAME.PHONE_NUMBER]: user => get(user, ['contactInfo', 'phone']),
+ [COLUMNS_NAME.MOBILE_NUMBER]: user => get(user, ['contactInfo', 'mobilePhone']),
+ [COLUMNS_NAME.ADDRESS]: user => {
+ const addressInfo = get(user, 'addressInfo');
+ return Object.values(addressInfo).join(',');
+ },
+ [COLUMNS_NAME.EMAIL_COMMUNICATION_PREFERENCES]: user => get(user, ['preferredEmailCommunication']).join(','),
+ [COLUMNS_NAME.SUBMISSION_DATE]: user => {
+ const submissionDate = get(user, ['metadata', 'updatedDate']);
+ return `${intl.formatDate(submissionDate)}, ${intl.formatTime(submissionDate)}`;
+ },
+ [COLUMNS_NAME.EMAIL_VERIFICATION]: user => {
+ const isEmailVerified = get(user, 'isEmailVerified');
+ if (isEmailVerified) return intl.formatMessage({ id: 'ui-users.stagingRecords.activated' });
+ else return intl.formatMessage({ id: 'ui-users.stagingRecords.notActivated' });
+ }
+ });
+
+ return (
+
+ );
+};
+
+PatronsPreRegistrationList.propTypes = {
+ isEmptyMessage: PropTypes.node.isRequired,
+ totalCount: PropTypes.number.isRequired,
+ data: PropTypes.arrayOf(PropTypes.object).isRequired,
+ onNeedMoreData: PropTypes.func.isRequired,
+};
+
+export default PatronsPreRegistrationList;
diff --git a/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.test.js b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.test.js
new file mode 100644
index 000000000..aa1634ba1
--- /dev/null
+++ b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationList.test.js
@@ -0,0 +1,46 @@
+import {
+ screen,
+ render,
+} from '@folio/jest-config-stripes/testing-library/react';
+
+import preRegistrationRecords from 'fixtures/preRegistrationRecords';
+
+import { MultiColumnList } from '@folio/stripes/components';
+
+import PatronsPreRegistrationList from './PatronsPreRegistrationList';
+
+const defaultProps = {
+ data: [],
+ isEmptyMessage: 'empty message',
+ totalCount: 0,
+ onNeedMoreData: jest.fn(),
+};
+
+describe('PatronsPreRegistrationList', () => {
+ it('should render the component', () => {
+ render();
+
+ expect(screen.getByTestId('PatronsPreRegistrationsList')).toBeVisible();
+ });
+
+ it('should be called with correct props', () => {
+ const expectedProps = {
+ autosize: true,
+ contentData: preRegistrationRecords,
+ id: 'PatronsPreRegistrationsList',
+ pageAmount: 100,
+ pagingType: 'prev-next',
+ totalCount: 1,
+ columnMapping: expect.any(Object),
+ formatter: expect.any(Object),
+ };
+ const props = {
+ ...defaultProps,
+ totalCount: 1,
+ data: preRegistrationRecords,
+ };
+
+ render();
+ expect(MultiColumnList).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {});
+ });
+});
diff --git a/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.js b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.js
new file mode 100644
index 000000000..a3081a438
--- /dev/null
+++ b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.js
@@ -0,0 +1,174 @@
+import { useState } from 'react';
+import PropTypes from 'prop-types';
+import { FormattedMessage, useIntl } from 'react-intl';
+import { noop } from 'lodash';
+
+import {
+ Pane,
+ PaneMenu,
+ Paneset,
+ Button,
+ Icon,
+ SearchField,
+} from '@folio/stripes/components';
+import {
+ SearchAndSortQuery,
+ SearchAndSortNoResultsMessage as NoResultsMessage,
+ CollapseFilterPaneButton,
+ ExpandFilterPaneButton,
+} from '@folio/stripes/smart-components';
+
+import PatronsPreRegistrationList from './PatronsPreRegistrationList';
+
+const PatronsPreRegistrationListContainer = ({
+ queryGetter,
+ onClose,
+ source,
+ data,
+ onNeedMoreData
+}) => {
+ const intl = useIntl();
+ const [isSearchPaneVisible, setIsSearchPaneVisible] = useState(true);
+ const query = queryGetter ? queryGetter() || {} : {};
+ const count = source ? source.totalCount() : 0;
+ const emptyMessage = source
+ ?
+ : null;
+
+ let resultPaneSub = ;
+
+ if (source && source.loaded()) {
+ resultPaneSub = ;
+ }
+
+ const toggleSearchPane = () => {
+ setIsSearchPaneVisible(prev => !prev);
+ };
+
+ const firstMenu = () => {
+ if (!isSearchPaneVisible) {
+ return (
+
+
+
+ );
+ } else {
+ return null;
+ }
+ };
+
+ return (
+
+ {
+ ({
+ getSearchHandlers,
+ onSubmitSearch,
+ searchValue,
+ resetAll,
+ }) => {
+ const isResetButtonDisabled = !searchValue.query;
+
+ return (
+
+ {
+ isSearchPaneVisible && (
+ }
+ lastMenu={
+
+
+
+ }
+ >
+
+
+ )
+ }
+ }
+ paneSub={resultPaneSub}
+ firstMenu={firstMenu()}
+ defaultWidth="fill"
+ dismissible
+ onClose={onClose}
+ padContent={false}
+ noOverflow
+ >
+
+
+
+ );
+ }
+ }
+
+ );
+};
+
+PatronsPreRegistrationListContainer.propTypes = {
+ onNeedMoreData: PropTypes.func.isRequired,
+ onClose: PropTypes.func.isRequired,
+ queryGetter: PropTypes.func.isRequired,
+ source: PropTypes.object.isRequired,
+ data: PropTypes.object.isRequired,
+};
+
+export default PatronsPreRegistrationListContainer;
diff --git a/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.test.js b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.test.js
new file mode 100644
index 000000000..5c5e7adf2
--- /dev/null
+++ b/src/views/PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer.test.js
@@ -0,0 +1,111 @@
+import {
+ screen,
+ waitFor,
+} from '@folio/jest-config-stripes/testing-library/react';
+import userEvent from '@folio/jest-config-stripes/testing-library/user-event';
+
+import renderWithRouter from 'helpers/renderWithRouter';
+import '../../../test/jest/__mock__/matchMedia.mock';
+
+import PatronsPreRegistrationListContainer from './PatronsPreRegistrationListContainer';
+
+jest.unmock('@folio/stripes/components');
+jest.mock('./PatronsPreRegistrationList', () => jest.fn(() => PatronsPreRegistrationList
));
+jest.mock('@folio/stripes/components', () => ({
+ ...jest.requireActual('@folio/stripes/components'),
+ SearchField: jest.fn((props) => (
+
+ )),
+}));
+
+const defaultProps = {
+ queryGetter: jest.fn(),
+ onClose: jest.fn(),
+ source: {
+ loaded: () => true,
+ totalCount: () => 0,
+ },
+ data: [],
+ onNeedMoreData: jest.fn(),
+};
+const renderComponent = (props) => renderWithRouter();
+
+describe('PatronsPreRegistrationListContainer', () => {
+ it('should render component', () => {
+ renderComponent();
+ expect(screen.getByText('ui-users.stagingRecords.list.searchResults')).toBeInTheDocument();
+ });
+
+ it('should display search pane', () => {
+ renderComponent();
+ expect(screen.getByText('ui-users.stagingRecords.list.search')).toBeInTheDocument();
+ });
+
+ it('should toggle search pane', async () => {
+ renderComponent();
+ expect(screen.getByText('ui-users.stagingRecords.list.search')).toBeInTheDocument();
+ const collapseButton = document.querySelector('[icon="caret-left"]');
+ await userEvent.click(collapseButton);
+ await waitFor(() => expect(screen.queryByText('ui-users.stagingRecords.list.search')).toBeNull());
+ });
+
+ it('should reset search on clicking reset button', async () => {
+ renderComponent();
+ const resetButton = document.querySelector('[id="preRegistrationListResetAllButton"]');
+ expect(resetButton).toBeDisabled();
+ await userEvent.type(document.querySelector('[id="stagingRecordsSearch"]'), 'record');
+ expect(resetButton).toBeEnabled();
+ await userEvent.click(resetButton);
+ await waitFor(() => expect(resetButton).toBeDisabled());
+ });
+
+ it('should render PatronsPreRegistrationList component', () => {
+ const props = {
+ ...defaultProps,
+ source: {
+ loaded: () => true,
+ totalCount: () => 1,
+ },
+ data: [
+ {
+ 'id': 'd717f710-ddf3-4208-9784-f443b74c40cd',
+ 'active': true,
+ 'generalInfo': {
+ 'firstName': 'new-record-2',
+ 'preferredFirstName': 'New Record',
+ 'middleName': '',
+ 'lastName': 'new-record-2'
+ },
+ 'addressInfo': {
+ 'addressLine0': '123 Main St',
+ 'addressLine1': 'Apt 4B',
+ 'city': 'Metropolis',
+ 'province': 'NY',
+ 'zip': '12345',
+ 'country': 'USA'
+ },
+ 'contactInfo': {
+ 'phone': '555-123456',
+ 'mobilePhone': '555-5678',
+ 'email': 'new-record-2@test.com'
+ },
+ 'preferredEmailCommunication': [
+ 'Support',
+ 'Programs'
+ ],
+ 'metadata': {
+ 'createdDate': '2024-04-29T13:29:41.711+00:00',
+ 'createdByUserId': '6d8a6bf2-5b5f-4168-aa2f-5c4e67b4b65c',
+ 'updatedDate': '2024-04-29T13:29:41.711+00:00',
+ 'updatedByUserId': '6d8a6bf2-5b5f-4168-aa2f-5c4e67b4b65c'
+ }
+ }
+ ],
+ };
+ renderComponent(props);
+
+ expect(screen.getByText('PatronsPreRegistrationList')).toBeInTheDocument();
+ });
+});
diff --git a/src/views/PatronsPreRegistrationListContainer/constants.js b/src/views/PatronsPreRegistrationListContainer/constants.js
new file mode 100644
index 000000000..d78313036
--- /dev/null
+++ b/src/views/PatronsPreRegistrationListContainer/constants.js
@@ -0,0 +1,46 @@
+import { FormattedMessage } from 'react-intl';
+
+export const COLUMNS_NAME = {
+ ACTION: 'ACTION',
+ FIRST_NAME: 'personal.firstName',
+ LAST_NAME: 'personal.lastName',
+ MIDDLE_NAME: 'middleName',
+ PREFERRED_FIRST_NAME: 'personal.preferredFirstName',
+ EMAIL: 'personal.email',
+ PHONE_NUMBER: 'personal.phone',
+ MOBILE_NUMBER: 'personal.mobilePhone',
+ ADDRESS: 'address',
+ EMAIL_COMMUNICATION_PREFERENCES: 'emailCommunicationPreferences',
+ SUBMISSION_DATE: 'submissionDate',
+ EMAIL_VERIFICATION: 'emailVerification'
+};
+
+export const visibleColumns = [
+ COLUMNS_NAME.ACTION,
+ COLUMNS_NAME.FIRST_NAME,
+ COLUMNS_NAME.LAST_NAME,
+ COLUMNS_NAME.MIDDLE_NAME,
+ COLUMNS_NAME.PREFERRED_FIRST_NAME,
+ COLUMNS_NAME.EMAIL,
+ COLUMNS_NAME.PHONE_NUMBER,
+ COLUMNS_NAME.MOBILE_NUMBER,
+ COLUMNS_NAME.ADDRESS,
+ COLUMNS_NAME.EMAIL_COMMUNICATION_PREFERENCES,
+ COLUMNS_NAME.SUBMISSION_DATE,
+ COLUMNS_NAME.EMAIL_VERIFICATION
+];
+
+export const columnMapping = {
+ [COLUMNS_NAME.ACTION]: ,
+ [COLUMNS_NAME.FIRST_NAME]: ,
+ [COLUMNS_NAME.LAST_NAME]: ,
+ [COLUMNS_NAME.MIDDLE_NAME]: ,
+ [COLUMNS_NAME.PREFERRED_FIRST_NAME]: ,
+ [COLUMNS_NAME.EMAIL]: ,
+ [COLUMNS_NAME.PHONE_NUMBER]: ,
+ [COLUMNS_NAME.MOBILE_NUMBER]: ,
+ [COLUMNS_NAME.ADDRESS]: ,
+ [COLUMNS_NAME.EMAIL_COMMUNICATION_PREFERENCES]: ,
+ [COLUMNS_NAME.SUBMISSION_DATE]: ,
+ [COLUMNS_NAME.EMAIL_VERIFICATION]: ,
+};
diff --git a/src/views/UserSearch/UserSearch.js b/src/views/UserSearch/UserSearch.js
index 6d1648761..925b57e8b 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 LinkToPatronPreRegistrations from '../../components/LinkToPatronPreRegistrations';
import PatronNoticePrintJobsLink from '../../components/PatronNoticePrintJobsLink';
import Filters from './Filters';
@@ -293,6 +294,7 @@ class UserSearch extends React.Component {
+
{
useCustomFields: jest.fn(() => [[customField]]),
};
});
+jest.mock('../../components/LinkToPatronPreRegistrations', () => {
+ return () => LinkToPatronPreRegistrations
;
+});
const defaultProps = {
mutator: {},
@@ -28,6 +32,7 @@ const defaultProps = {
},
stripes: {
hasInterface: jest.fn(),
+ hasPerm: () => true,
},
location: {},
history: {},
@@ -47,6 +52,20 @@ const renderComponent = (props) => renderWithRouter( {
it('should render component', () => {
renderComponent();
- expect(screen.getByText('ui-users.status')).toBeTruthy();
+ expect(screen.getByText('ui-users.userSearchResults')).toBeInTheDocument();
+ });
+
+ it('should render actions menu', () => {
+ renderComponent();
+ expect(screen.getByText('ui-users.actions')).toBeInTheDocument();
+ });
+
+ it('should display "Search patron preregistration records"', async () => {
+ renderComponent();
+ const actionsButton = screen.getByText('ui-users.actions');
+
+ await userEvent.click(actionsButton);
+
+ expect(screen.getByText('LinkToPatronPreRegistrations')).toBeInTheDocument();
});
});
diff --git a/src/views/index.js b/src/views/index.js
index 3012560a8..53525605a 100644
--- a/src/views/index.js
+++ b/src/views/index.js
@@ -9,3 +9,4 @@ export { default as AccountsListing } from './AccountsListing/AccountsListing';
export { default as NoteCreatePage } from './Notes/NoteCreatePage';
export { default as NoteEditPage } from './Notes/NoteEditPage';
export { default as NoteViewPage } from './Notes/NoteViewPage';
+export { default as PatronsPreRegistrationList } from './PatronsPreRegistrationListContainer/PatronsPreRegistrationListContainer';
diff --git a/test/jest/fixtures/preRegistrationRecords.json b/test/jest/fixtures/preRegistrationRecords.json
new file mode 100644
index 000000000..b61479978
--- /dev/null
+++ b/test/jest/fixtures/preRegistrationRecords.json
@@ -0,0 +1,36 @@
+[
+ {
+ "id": "58fcba1e-87d2-4ae7-a0a3-36d2425837c2",
+ "isEmailVerified": true,
+ "status": "TIER-2",
+ "generalInfo": {
+ "firstName": "new-record-1",
+ "preferredFirstName": "New Record",
+ "middleName": "",
+ "lastName": "new-record-1"
+ },
+ "addressInfo": {
+ "addressLine0": "123 Main St",
+ "addressLine1": "Apt 4B",
+ "city": "Metropolis",
+ "province": "NY",
+ "zip": "12345",
+ "country": "USA"
+ },
+ "contactInfo": {
+ "phone": "555-123456",
+ "mobilePhone": "555-5678",
+ "email": "new-record-1@test.com"
+ },
+ "preferredEmailCommunication": [
+ "Support",
+ "Programs"
+ ],
+ "metadata": {
+ "createdDate": "2024-04-29T13:29:41.711+00:00",
+ "createdByUserId": "6d8a6bf2-5b5f-4168-aa2f-5c4e67b4b65c",
+ "updatedDate": "2024-04-29T13:29:41.711+00:00",
+ "updatedByUserId": "6d8a6bf2-5b5f-4168-aa2f-5c4e67b4b65c"
+ }
+ }
+]
diff --git a/translations/ui-users/en.json b/translations/ui-users/en.json
index bbdef36af..775db9ba1 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.preRegistrationRecords": "Search patron pre-registration records",
"actionMenu.patronNoticePrintJobs": "View patron notice print jobs (PDF)",
"dueDate": "Due date",
"loanDate": "Loan date",
@@ -1109,6 +1110,7 @@
"permission.remove-patron-notice-print-jobs": "Users: View and remove patron notice print jobs",
"permission.view-reading-room-access": "Users: Can view reading room access",
"permission.edit-reading-room-access": "Users: Can view, and edit reading room access",
+ "permission.patron-pre-registrations-view": "Users: Can view patron pre-registration data",
"bulkClaimReturned.item.title": "Title",
"bulkClaimReturned.preConfirm": "Confirm claim returned",
"bulkClaimReturned.postConfirm": "Claim returned confirmation",
@@ -1192,7 +1194,6 @@
"patronNoticePrintJobs.updated": "Updated",
"patronNoticePrintJobs.created": "Created",
"patronNoticePrintJobs.errors.pdf": "'PDF generation failed",
-
"roles.userRoles": "User roles",
"roles.empty": "No user roles found",
"roles.deleteRole": "Delete user role",
@@ -1206,5 +1207,23 @@
"roles.modal.unassignAll.label": "You are unassigning all user roles {roles} Are you sure?
",
"roles.modal.search.header": "Search & filter",
"roles.modal.filter.status.label": "Role assigment status",
- "roles.modal.header": "Select user roles"
+ "roles.modal.header": "Select user roles",
+ "stagingRecords.list.search": "Search",
+ "stagingRecords.search.placeholder": "Search by name or email",
+ "stagingRecords.search.label": "Staging records search",
+ "stagingRecords.list.searchResults": "Patron pre-registration record results",
+ "stagingRecords.list.columnNames.action": "Action",
+ "stagingRecords.list.columnNames.firstName": "First name",
+ "stagingRecords.list.columnNames.lastName": "Last name",
+ "stagingRecords.list.columnNames.middleName": "Middle name",
+ "stagingRecords.list.columnNames.preferredFirstName": "Preferred first name",
+ "stagingRecords.list.columnNames.email": "Email",
+ "stagingRecords.list.columnNames.phoneNumber": "Phone number",
+ "stagingRecords.list.columnNames.mobileNumber": "Mobile number",
+ "stagingRecords.list.columnNames.address": "Address",
+ "stagingRecords.list.columnNames.emailCommunicationPreferences": "Email communication preferences",
+ "stagingRecords.list.columnNames.submissionDate": "Submission date",
+ "stagingRecords.list.columnNames.emailVerification": "Email verification",
+ "stagingRecords.activated": "Activated",
+ "stagingRecords.notActivated": "Not activated"
}