Skip to content

Commit

Permalink
Fixed a slot size selecting issue in reservation edit page (#314)
Browse files Browse the repository at this point in the history
Previously when admin selected to edit a reservation via manage reservations page using reservation page, it was possible to select time slot incorrectly. This happened because reservation page did not get correct slot size info from manage reservations page.

This change fixes the issue by making sure slot size is given correctly when using reservation page to edit reservations.
  • Loading branch information
SanttuA authored Apr 11, 2024
1 parent 75aab82 commit e603aa4
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 20 deletions.
41 changes: 32 additions & 9 deletions app/pages/manage-reservations/ManageReservationsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -51,6 +51,8 @@ class ManageReservationsPage extends React.Component {
showMassCancel: false,
showConfirmCash: false,
selectedReservation: null,
openingEditPage: false,
editPayload: {},
};

this.handleFetchReservations = this.handleFetchReservations.bind(this);
Expand All @@ -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() {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -40,6 +40,8 @@ describe('ManageReservationsFilters', () => {
locale: 'fi',
units: [],
reservations: [],
resources: {},
isFetchingResource: false,
reservationsTotalCount: 0,
isFetchingReservations: false,
isFetchingUnits: false,
Expand Down Expand Up @@ -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');
Expand All @@ -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', () => {
Expand Down Expand Up @@ -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();
Expand All @@ -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 });
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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();
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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';

Expand Down Expand Up @@ -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('');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -31,6 +31,8 @@ const manageReservationsPageSelector = createStructuredSelector({
units: unitsArraySelector,
reservations: reservationsSelector,
reservationsTotalCount: reservationsCountSelector,
resources: resourcesSelector,
isFetchingResource: requestIsActiveSelectorFactory(ActionTypes.API.RESOURCE_GET_REQUEST),
fontSize: fontSizeSelector,
});

Expand Down
12 changes: 12 additions & 0 deletions app/pages/manage-reservations/manageReservationsPageUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 '';
}

0 comments on commit e603aa4

Please sign in to comment.