diff --git a/CHANGELOG.md b/CHANGELOG.md index 1273f73..530d32b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Persist filters when changing between actions. Refs UIREQMED-44. * Implement functionality for editing mediated request. Refs UIREQMED-22. * *BREAKING* Migrate to new `mod-circulation-bff` endpoints. Refs UIREQMED-39. +* Add Decline Action for Mediated request with status of New - Awaiting confirmation. Refs UIREQMED-42. ## [1.1.0](https://github.com/folio-org/ui-requests-mediated/tree/v1.1.0) (2024-10-30) * Update github actions. Refs UIREQMED-14. diff --git a/package.json b/package.json index 6c2983d..706896a 100644 --- a/package.json +++ b/package.json @@ -171,7 +171,7 @@ ], "subPermissions": [ "ui-requests-mediated.view", - "requests-mediated.mediated-request.item.delete" + "requests-mediated.decline-mediated-request.execute" ], "visible": true }, diff --git a/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/MediatedRequestsDetail.js b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/MediatedRequestsDetail.js index 8c4ac08..142f219 100644 --- a/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/MediatedRequestsDetail.js +++ b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/MediatedRequestsDetail.js @@ -1,3 +1,6 @@ +import { + useState, +} from 'react'; import PropTypes from 'prop-types'; import { useHistory, @@ -14,6 +17,8 @@ import { import { IfPermission, TitleManager, + useOkapiKy, + useStripes, } from '@folio/stripes/core'; import { Button, @@ -31,6 +36,7 @@ import TitleInformation from '../TitleInformation'; import MediatedRequestInformation from '../MediatedRequestInformation'; import ItemDetail from '../ItemDetail'; import UserDetail from '../UserDetail'; +import DeclineModal from './components/DeclineModal'; import { useMediatedRequestById, useUserById, @@ -64,15 +70,19 @@ const MediatedRequestsDetail = ({ stripes, patronGroups, setRequest, + update, }) => { const history = useHistory(); const location = useLocation(); + const ky = useOkapiKy(); const mediatedRequestIdFromPathname = location.pathname.substring(location.pathname.lastIndexOf('/') + 1); const { formatMessage } = useIntl(); const { mediatedRequest, isFetching, + shouldUpdate, + setShouldUpdate, } = useMediatedRequestById(mediatedRequestIdFromPathname); const { userData } = useUserById(mediatedRequest?.requesterId, isFetching); const { servicePoints } = useServicePoints(); @@ -80,6 +90,20 @@ const MediatedRequestsDetail = ({ setRequest(mediatedRequest); + const [declineModalOpen, setDeclineModalOpen] = useState(false); + const onOpenDeclineModal = () => setDeclineModalOpen(true); + const onConfirmDeclineModal = () => { + ky.post(`requests-mediated/mediated-requests/${mediatedRequestIdFromPathname}/decline`) + .then(() => { + update(); + setShouldUpdate(shouldUpdate + 1); + setDeclineModalOpen(false); + }) + .catch(() => { + setDeclineModalOpen(false); + }); + }; + const onCloseDeclineModal = () => setDeclineModalOpen(false); const isActionMenuVisible = () => ( get(mediatedRequest, MEDIATED_REQUESTS_RECORD_FIELD_PATH[MEDIATED_REQUESTS_RECORD_FIELD_NAME.STATUS], DEFAULT_VIEW_VALUE) === MEDIATED_REQUEST_STATUS.NEW_AWAITING_CONFIRMATION ? stripes.hasPerm('ui-requests-mediated.requests-mediated.view-confirm.execute') || stripes.hasPerm('ui-requests-mediated.requests-mediated.view-create-edit.execute') || stripes.hasPerm('ui-requests-mediated.requests-mediated.view-decline.execute') @@ -114,7 +138,10 @@ const MediatedRequestsDetail = ({ + + + ); + + return ( + } + open={open} + size="small" + footer={footer} + onClose={onClose} + > + + + ); +}; + +DeclineModal.propTypes = { + open: PropTypes.bool.isRequired, + title: PropTypes.string.isRequired, + onConfirm: PropTypes.func.isRequired, + onClose: PropTypes.func.isRequired, +}; + +export default DeclineModal; diff --git a/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/DeclineModal.test.js b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/DeclineModal.test.js new file mode 100644 index 0000000..113b513 --- /dev/null +++ b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/DeclineModal.test.js @@ -0,0 +1,76 @@ +import { + render, + screen, + fireEvent, +} from '@folio/jest-config-stripes/testing-library/react'; + +import DeclineModal from './DeclineModal'; + +const testIds = { + confirmButton: 'confirmButton', + backButton: 'backButton', +}; +const messageIds = { + title: 'ui-requests-mediated.declineModal.title', + message: 'ui-requests-mediated.declineModal.message', + confirmButton: 'ui-requests-mediated.declineModal.confirm', + backButton: 'ui-requests-mediated.declineModal.back', +}; +const defaultProps = { + open: true, + title: 'Title', + onConfirm: jest.fn(), + onClose: jest.fn(), +}; + +describe('DeclineModal', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + beforeEach(() => { + render( + + ); + }); + + it('should render title', () => { + expect(screen.getByText(messageIds.title)).toBeVisible(); + }); + + it('should render message', () => { + expect(screen.getByText(messageIds.message)).toBeVisible(); + }); + + describe('Confirm button', () => { + it('should render confirm button', () => { + expect(screen.getByTestId(testIds.confirmButton)).toBeVisible(); + }); + + it('should render confirm button text', () => { + expect(screen.getByText(messageIds.confirmButton)).toBeVisible(); + }); + + it('should call onConfirm', () => { + fireEvent.click(screen.getByTestId(testIds.confirmButton)); + + expect(defaultProps.onConfirm).toHaveBeenCalled(); + }); + }); + + describe('Back button', () => { + it('should render back button', () => { + expect(screen.getByTestId(testIds.backButton)).toBeVisible(); + }); + + it('should render back button text', () => { + expect(screen.getByText(messageIds.backButton)).toBeVisible(); + }); + + it('should call onClose', () => { + fireEvent.click(screen.getByTestId(testIds.backButton)); + + expect(defaultProps.onClose).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/index.js b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/index.js new file mode 100644 index 0000000..9faba9e --- /dev/null +++ b/src/components/MediatedRequestsActivities/components/MediatedRequestsDetail/components/DeclineModal/index.js @@ -0,0 +1 @@ +export { default } from './DeclineModal'; diff --git a/src/hooks/useMediatedRequestById/useMediatedRequestById.js b/src/hooks/useMediatedRequestById/useMediatedRequestById.js index 16e4e03..792e71d 100644 --- a/src/hooks/useMediatedRequestById/useMediatedRequestById.js +++ b/src/hooks/useMediatedRequestById/useMediatedRequestById.js @@ -1,3 +1,7 @@ +import { + useState, +} from 'react'; + import { useQuery } from 'react-query'; import { @@ -6,13 +10,15 @@ import { } from '@folio/stripes/core'; const useMediatedRequestById = (mediatedRequestId) => { + const [shouldUpdate, setShouldUpdate] = useState(0); + const ky = useOkapiKy(); const [namespace] = useNamespace({ key: 'mediatedRequest' }); const { data, isFetching, } = useQuery( - [namespace, mediatedRequestId], + [namespace, mediatedRequestId, shouldUpdate], () => ky.get(`requests-mediated/mediated-requests/${mediatedRequestId}`).json(), { enabled: Boolean(mediatedRequestId) }, ); @@ -20,6 +26,8 @@ const useMediatedRequestById = (mediatedRequestId) => { return { isFetching, mediatedRequest: data, + shouldUpdate, + setShouldUpdate, }; }; diff --git a/src/routes/MediatedRequestsActivitiesContainer.js b/src/routes/MediatedRequestsActivitiesContainer.js index b983827..a047bdb 100644 --- a/src/routes/MediatedRequestsActivitiesContainer.js +++ b/src/routes/MediatedRequestsActivitiesContainer.js @@ -62,6 +62,8 @@ class MediatedRequestsActivitiesContainer extends React.Component { query: buildQuery, }, }, + // shouldRefresh: () => true, + resourceShouldRefresh: true, throwErrors: false, }, reportRecords: { @@ -161,6 +163,21 @@ class MediatedRequestsActivitiesContainer extends React.Component { } }; + update = () => { + const { + location, + history, + } = this.props; + const { + resources: { + query, + }, + } = this.source; + const url = buildUrl(location, query); + + history.push(url); + }; + render() { if (this.source) { this.source.update(this.props, MEDIATED_REQUESTS_RECORDS_NAME); @@ -176,7 +193,13 @@ class MediatedRequestsActivitiesContainer extends React.Component { mutator={this.props.mutator} settings={this.props.settings} > - {this.props.children} + { + React.Children.map( + this.props.children, child => React.cloneElement(child, { + update: this.update, + }) + ) + } ); } diff --git a/translations/ui-requests-mediated/en.json b/translations/ui-requests-mediated/en.json index c18d55a..5e328b3 100644 --- a/translations/ui-requests-mediated/en.json +++ b/translations/ui-requests-mediated/en.json @@ -34,6 +34,11 @@ "confirmItem.errorModal.sendItemInTransit.message": "No \"Send item in transit\" transaction could be found for this item", "confirmItem.errorModal.close": "Close", + "declineModal.title": "Confirm Mediated request decline", + "declineModal.message": "{title} will be declined", + "declineModal.confirm": "Confirm", + "declineModal.back": "Back", + "confirmItemList.columnName.arrivalDate": "Arrival date", "confirmItemList.columnName.inTransitDate": "In transit date", "confirmItemList.columnName.title": "Title",