diff --git a/app/pages/manage-reservations/ManageReservationsPage.js b/app/pages/manage-reservations/ManageReservationsPage.js index 875ba45da..94c5dfc6b 100644 --- a/app/pages/manage-reservations/ManageReservationsPage.js +++ b/app/pages/manage-reservations/ManageReservationsPage.js @@ -34,7 +34,7 @@ import { denyPreliminaryReservation } from 'actions/reservationActions'; import manageReservationsPageSelector from './manageReservationsPageSelector'; -import { getFilteredReservations } from './manageReservationsPageUtils'; +import { getFilteredReservations, getResourceSlotSize } from './manageReservationsPageUtils'; import ReservationInfoModal from 'shared/modals/reservation-info'; import ReservationCancelModal from 'shared/modals/reservation-cancel'; import PageResultsText from './PageResultsText'; @@ -51,6 +51,8 @@ class ManageReservationsPage extends React.Component { showMassCancel: false, showConfirmCash: false, selectedReservation: null, + openingEditPage: false, + editPayload: {}, }; this.handleFetchReservations = this.handleFetchReservations.bind(this); @@ -65,6 +67,7 @@ class ManageReservationsPage extends React.Component { this.handleShowConfirmCash = this.handleShowConfirmCash.bind(this); this.handleHideConfirmCash = this.handleHideConfirmCash.bind(this); this.handleConfirmCash = this.handleConfirmCash.bind(this); + this.handleOpenEditPage = this.handleOpenEditPage.bind(this); } handleShowMassCancel() { @@ -93,6 +96,12 @@ class ManageReservationsPage extends React.Component { if (prevProps.location !== location) { this.handleFetchReservations(); } + if (prevProps.isFetchingResource && !this.props.isFetchingResource) { + const { openingEditPage } = this.state; + if (openingEditPage) { + this.handleOpenEditPage(); + } + } } onSearchFiltersChange(filters) { @@ -149,21 +158,33 @@ class ManageReservationsPage extends React.Component { actions.showReservationInfoModal(reservation); } - // opens reservation page in edit mode + // starts process of opening reservation edit page handleEditClick(reservation) { - const { history, actions } = this.props; - const normalizedReservation = Object.assign( {}, reservation, { resource: reservation.resource.id } ); + this.handleFetchResource(reservation.resource.id, reservation.begin); + const payload = { reservation: normalizedReservation }; + this.setState({ openingEditPage: true, editPayload: payload }); + } + + handleOpenEditPage() { + const { actions, resources, history } = this.props; + const { editPayload } = this.state; + + this.setState({ openingEditPage: false }); + // clear old selected reservations before selecting new reservation to edit actions.clearReservations(); - // fetch resource before changing page to make sure reservation page has - // all needed info to function - this.handleFetchResource(reservation.resource.id, reservation.begin); - actions.editReservation({ reservation: normalizedReservation }); - const nextUrl = `${getEditReservationUrl(normalizedReservation)}&path=manage-reservations`; + const payload = { ...editPayload }; + const slotSize = getResourceSlotSize(resources, payload.reservation.resource); + if (slotSize) { + payload.slotSize = slotSize; + } + + actions.editReservation(payload); + const nextUrl = `${getEditReservationUrl(editPayload.reservation)}&path=manage-reservations`; history.push(nextUrl); } @@ -315,6 +336,8 @@ ManageReservationsPage.propTypes = { units: PropTypes.array, reservations: PropTypes.array, reservationsTotalCount: PropTypes.number.isRequired, + resources: PropTypes.object, + isFetchingResource: PropTypes.bool, isFetchingReservations: PropTypes.bool, isFetchingUnits: PropTypes.bool, fontSize: PropTypes.string, diff --git a/app/pages/manage-reservations/__tests__/ManageReservationsPage.spec.js b/app/pages/manage-reservations/__tests__/ManageReservationsPage.spec.js index 85dd0dce5..02c6f1109 100644 --- a/app/pages/manage-reservations/__tests__/ManageReservationsPage.spec.js +++ b/app/pages/manage-reservations/__tests__/ManageReservationsPage.spec.js @@ -21,7 +21,7 @@ import MassCancelModal from '../../../shared/modals/reservation-mass-cancel/Mass import ConfirmCashModal from '../../../shared/modals/reservation-confirm-cash/ConfirmCashModal'; -describe('ManageReservationsFilters', () => { +describe('ManageReservationsPage', () => { const defaultProps = { actions: { clearReservations: jest.fn(), @@ -40,6 +40,8 @@ describe('ManageReservationsFilters', () => { locale: 'fi', units: [], reservations: [], + resources: {}, + isFetchingResource: false, reservationsTotalCount: 0, isFetchingReservations: false, isFetchingUnits: false, @@ -242,6 +244,12 @@ describe('ManageReservationsFilters', () => { }); describe('componentDidUpdate', () => { + const resource = { id: 'test-id' }; + const reservation = Reservation.build({ resource }); + const normalizedReservation = Object.assign( + {}, reservation, { resource: reservation.resource.id } + ); + test('calls handleFetchReservations when location changes', () => { const instance = getWrapper({ location: { search: '' } }).instance(); const spy = jest.spyOn(instance, 'handleFetchReservations'); @@ -256,6 +264,24 @@ describe('ManageReservationsFilters', () => { instance.componentDidUpdate({ location }); expect(spy).toHaveBeenCalledTimes(0); }); + + test('calls handleOpenEditPage when resource fetch completes and opening edit page', () => { + const instance = getWrapper({ isFetchingResource: false }).instance(); + instance.state.openingEditPage = true; + instance.state.editPayload = { reservation: normalizedReservation }; + const spy = jest.spyOn(instance, 'handleOpenEditPage'); + instance.componentDidUpdate({ isFetchingResource: true }); + expect(spy).toHaveBeenCalledTimes(1); + }); + + test('does not call handleOpenEditPage when not opening edit page', () => { + const instance = getWrapper({ isFetchingResource: false }).instance(); + instance.state.openingEditPage = false; + instance.state.editPayload = { reservation: normalizedReservation }; + const spy = jest.spyOn(instance, 'handleOpenEditPage'); + instance.componentDidUpdate({ isFetchingResource: true }); + expect(spy).toHaveBeenCalledTimes(0); + }); }); describe('onSearchFiltersChange', () => { @@ -343,11 +369,6 @@ describe('ManageReservationsFilters', () => { const normalizedReservation = Object.assign( {}, reservation, { resource: reservation.resource.id } ); - test('calls clearReservations', () => { - const instance = getWrapper().instance(); - instance.handleEditClick(reservation); - expect(defaultProps.actions.clearReservations.mock.calls.length).toBe(1); - }); test('calls handleFetchResource with correct params', () => { const instance = getWrapper().instance(); @@ -357,9 +378,48 @@ describe('ManageReservationsFilters', () => { expect(spy).toHaveBeenCalledWith(resource.id, reservation.begin); }); - test('calls editReservation with correct params', () => { + test('calls setState with correct params', () => { const instance = getWrapper().instance(); + const spy = jest.spyOn(instance, 'setState'); instance.handleEditClick(reservation); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy.mock.calls[0][0]).toStrictEqual({ + openingEditPage: true, + editPayload: { reservation: normalizedReservation } + }); + }); + }); + + describe('handleOpenEditPage', () => { + const resource = { id: 'test-id' }; + const reservation = Reservation.build({ resource }); + const normalizedReservation = Object.assign( + {}, reservation, { resource: reservation.resource.id } + ); + + test('calls setState with correct params', () => { + const instance = getWrapper().instance(); + instance.state.editPayload = { reservation: normalizedReservation }; + const spy = jest.spyOn(instance, 'setState'); + instance.handleOpenEditPage(); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy.mock.calls[0][0]).toStrictEqual( + { openingEditPage: false } + ); + }); + + test('calls clearReservations', () => { + const instance = getWrapper().instance(); + instance.state.editPayload = { reservation: normalizedReservation }; + instance.handleOpenEditPage(); + expect(defaultProps.actions.clearReservations.mock.calls.length).toBe(1); + }); + + test('calls editReservation with correct params', () => { + const instance = getWrapper().instance(); + instance.state.editPayload = { reservation: normalizedReservation }; + instance.handleOpenEditPage(); + instance.state.editPayload = { reservation: normalizedReservation }; expect(defaultProps.actions.editReservation.mock.calls.length).toBe(1); expect(defaultProps.actions.editReservation.mock.calls[0][0]) .toStrictEqual({ reservation: normalizedReservation }); @@ -368,7 +428,8 @@ describe('ManageReservationsFilters', () => { test('calls history push with correct params', () => { const history = { push: jest.fn() }; const instance = getWrapper({ history }).instance(); - instance.handleEditClick(reservation); + instance.state.editPayload = { reservation: normalizedReservation }; + instance.handleOpenEditPage(); const expectedParam = `${getEditReservationUrl(normalizedReservation)}&path=manage-reservations`; expect(history.push.mock.calls.length).toBe(1); expect(history.push.mock.calls[0][0]).toBe(expectedParam); diff --git a/app/pages/manage-reservations/__tests__/manageReservationsPageSelector.spec.js b/app/pages/manage-reservations/__tests__/manageReservationsPageSelector.spec.js index 40ac45036..2d30a5456 100644 --- a/app/pages/manage-reservations/__tests__/manageReservationsPageSelector.spec.js +++ b/app/pages/manage-reservations/__tests__/manageReservationsPageSelector.spec.js @@ -10,7 +10,7 @@ jest.mock('state/selectors/dataSelectors', () => { }; }); -describe('/pages/favorites/favoritesPageSelector', () => { +describe('/pages/manage-reservations/manageReservationsPageSelector', () => { function getSelector() { const state = getState(); return manageReservationsPageSelector(state); @@ -48,6 +48,14 @@ describe('/pages/favorites/favoritesPageSelector', () => { expect(getSelector().reservationsTotalCount).toBeDefined(); }); + test('returns resources', () => { + expect(getSelector().resources).toBeDefined(); + }); + + test('returns isFetchingResource', () => { + expect(getSelector().isFetchingResource).toBeDefined(); + }); + test('returns fontSize', () => { expect(getSelector().fontSize).toBeDefined(); }); diff --git a/app/pages/manage-reservations/__tests__/manageReservationsPageUtils.spec.js b/app/pages/manage-reservations/__tests__/manageReservationsPageUtils.spec.js index 23c6548d1..f1ef6aaf9 100644 --- a/app/pages/manage-reservations/__tests__/manageReservationsPageUtils.spec.js +++ b/app/pages/manage-reservations/__tests__/manageReservationsPageUtils.spec.js @@ -1,5 +1,7 @@ import constants from 'constants/AppConstants'; -import { getFilteredReservations, getHiddenReservationCount, getPageResultsText } from '../manageReservationsPageUtils'; +import { + getFilteredReservations, getHiddenReservationCount, getPageResultsText, getResourceSlotSize +} from '../manageReservationsPageUtils'; import Reservation from 'utils/fixtures/Reservation'; import Resource from 'utils/fixtures/Resource'; @@ -111,4 +113,36 @@ describe('pages/manage-reservations/manageReservationsPageUtils', () => { }); }); }); + + describe('getResourceSlotSize', () => { + const resources = { + 1: { + id: 'one', + slotSize: '01:00:00' + }, + 2: { + id: 'two', + slotSize: '01:30:00' + } + }; + + test('returns slotSize of resource', () => { + const resourceId = '1'; + expect(getResourceSlotSize(resources, resourceId)).toBe('01:00:00'); + }); + + test('returns empty string if resource is not found', () => { + const resourceId = '3'; + expect(getResourceSlotSize(resources, resourceId)).toBe(''); + }); + + test('returns empty string if resources is not given', () => { + const resourceId = '1'; + expect(getResourceSlotSize(undefined, resourceId)).toBe(''); + }); + + test('returns empty string if resource id is not given', () => { + expect(getResourceSlotSize(resources, undefined)).toBe(''); + }); + }); }); diff --git a/app/pages/manage-reservations/manageReservationsPageSelector.js b/app/pages/manage-reservations/manageReservationsPageSelector.js index a118b2512..8ae7ac2fa 100644 --- a/app/pages/manage-reservations/manageReservationsPageSelector.js +++ b/app/pages/manage-reservations/manageReservationsPageSelector.js @@ -3,7 +3,7 @@ import { createSelector, createStructuredSelector } from 'reselect'; import ActionTypes from 'constants/ActionTypes'; import requestIsActiveSelectorFactory from 'state/selectors/factories/requestIsActiveSelectorFactory'; import { isAdminSelector } from 'state/selectors/authSelectors'; -import { userFavouriteResourcesSelector, unitsSelector } from 'state/selectors/dataSelectors'; +import { userFavouriteResourcesSelector, unitsSelector, resourcesSelector } from 'state/selectors/dataSelectors'; import { currentLanguageSelector } from 'state/selectors/translationSelectors'; import { fontSizeSelector } from '../../state/selectors/accessibilitySelectors'; @@ -31,6 +31,8 @@ const manageReservationsPageSelector = createStructuredSelector({ units: unitsArraySelector, reservations: reservationsSelector, reservationsTotalCount: reservationsCountSelector, + resources: resourcesSelector, + isFetchingResource: requestIsActiveSelectorFactory(ActionTypes.API.RESOURCE_GET_REQUEST), fontSize: fontSizeSelector, }); diff --git a/app/pages/manage-reservations/manageReservationsPageUtils.js b/app/pages/manage-reservations/manageReservationsPageUtils.js index 9188e01d6..6acb83563 100644 --- a/app/pages/manage-reservations/manageReservationsPageUtils.js +++ b/app/pages/manage-reservations/manageReservationsPageUtils.js @@ -62,3 +62,15 @@ export function getPageResultsText(currentPage, resultsPerPage, currentPageResul const to = resultsPerPage * currentPage + currentPageResults; return `${from} - ${to} / ${totalResults}`; } + +/** + * Gets slot size of resource + * @param {object} resources + * @param {string} resourceId + * @returns {string} slot size if resource is found in resources, empty string otherwise. + */ +export function getResourceSlotSize(resources, resourceId) { + const resource = !!resources && resourceId ? resources[resourceId] : undefined; + if (resource) return resource.slotSize; + return ''; +}