Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added reservation type selecting for staff #287

Merged
merged 3 commits into from
Nov 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions app/constants/AppConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,17 @@ const constants = {
SMALL: '__font-size-small',
MEDIUM: '__font-size-medium',
LARGE: '__font-size-large',
},
RESERVATION_TYPE: {
LEGEND_TEXT_ID: 'ReservationType.legend',
LEGEND_HINT_TEXT_ID: 'ReservationType.legendHint',
TYPE_NAME: 'type',
NORMAL_VALUE: 'normal',
NORMAL_LABEL_ID: 'ReservationType.normal',
NORMAL_HINT_TEXT_ID: 'ReservationType.normalHint',
BLOCKED_VALUE: 'blocked',
BLOCKED_LABEL_ID: 'ReservationType.blocked',
BLOCKED_HINT_TEXT_ID: 'ReservationType.blockedHint',
}
};

Expand Down
7 changes: 7 additions & 0 deletions app/i18n/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@
"ReservationTermsModal.resourcePaymentTermsSubTitle": "Payment terms of {name}",
"ReservationTermsModal.resourceTermsTitle": "User regulations of the premises",
"ReservationTermsModal.resourceTermsSubTitle": "User regulations of {name}:",
"ReservationType.blocked": "Blocking",
"ReservationType.blockedHint": "This can be used for example to set breaks in the reservation calendar",
"ReservationType.label": "Reservation type",
"ReservationType.legend": "Choose reservation type",
"ReservationType.legendHint": "Use normal unless you want to intentionally block clients from reserving this time slot",
"ReservationType.normal": "Normal",
"ReservationType.normalHint": "Use this unless you want to block clients from making reservations in this time slot",
"ReservingRestrictedText.reservationAvailableBetween": "At the moment, you can reserve the premises from {today} to {until}.",
"ReservingRestrictedText.reservationRestricted": "You can reserve the space at the earliest {days} in advance.",
"ResourceAvailability.available": "Available right now",
Expand Down
7 changes: 7 additions & 0 deletions app/i18n/messages/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,13 @@
"ReservationTermsModal.resourcePaymentTermsSubTitle": "{name} koskevat maksuehdot:",
"ReservationTermsModal.resourceTermsTitle": "Tilan käyttösäännöt",
"ReservationTermsModal.resourceTermsSubTitle": "{name} koskevat käyttösäännöt:",
"ReservationType.blocked": "Blokkaava",
"ReservationType.blockedHint": "Tätä voidaan käyttää esimerkiksi taukojen asettamiseen varauskalenteriin",
"ReservationType.label": "Varauksen tyyppi",
"ReservationType.legend": "Valitse varauksen tyyppi",
"ReservationType.legendHint": "Käytä normaalia, ellet halua tarkoituksellisesti estää asiakkaita varaamasta tätä aikaväliä",
"ReservationType.normal": "Normaali",
"ReservationType.normalHint": "Käytä tätä, ellet halua estää asiakkaita varaamasta tätä aikaväliä",
"ReservingRestrictedText.reservationAvailableBetween": "Tällä hetkellä voit tehdä varauksen ajalle {today} – {until}.",
"ReservingRestrictedText.reservationRestricted": "Voit varata tilan aikaisintaan {days} päivää etukäteen.",
"ResourceAvailability.available": "Heti vapaa",
Expand Down
7 changes: 7 additions & 0 deletions app/i18n/messages/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,13 @@
"ReservationTermsModal.resourcePaymentTermsSubTitle": "{name} betalningsvillkor",
"ReservationTermsModal.resourceTermsTitle": "Utrymmets användningsregler",
"ReservationTermsModal.resourceTermsSubTitle": "användningsregler för {name}:",
"ReservationType.blocked": "Blockering",
"ReservationType.blockedHint": "Detta kan användas till exempel för att ställa in pauser i reservationskalendern",
"ReservationType.label": "Reservationstyp",
"ReservationType.legend": "Välj bokningstyp",
"ReservationType.legendHint": "Använd normalt om du inte vill blockera kunder från att boka den här tidsperioden med avsikt",
"ReservationType.normal": "Normal",
"ReservationType.normalHint": "Använd detta om du inte vill blockera kunder från att göra reservationer under den här tidsperioden",
"ReservingRestrictedText.reservationAvailableBetween": "För närvarande kan du göra en bokning för tiden {today} – {until}.",
"ReservingRestrictedText.reservationRestricted": "Du kan boka utrymmet tidigast {days} dagar i förväg.",
"ResourceAvailability.available": "Ledigt nu",
Expand Down
3 changes: 3 additions & 0 deletions app/pages/reservation/ReservationPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ class UnconnectedReservationPage extends Component {
uniqueCustomerGroups,
user,
history,
reservationType,
} = this.props;
const {
currentCustomerGroup, customerGroupError, currentPaymentMethod,
Expand Down Expand Up @@ -533,6 +534,7 @@ class UnconnectedReservationPage extends Component {
openResourceTermsModal={actions.openResourceTermsModal}
order={order}
reservation={reservationToEdit}
reservationType={reservationType}
resource={resource}
selectedTime={selectedTime}
state={this.props.state}
Expand Down Expand Up @@ -584,6 +586,7 @@ UnconnectedReservationPage.propTypes = {
isLoggedIn: PropTypes.bool.isRequired,
loginExpiresAt: PropTypes.number,
uniqueCustomerGroups: PropTypes.array.isRequired,
reservationType: PropTypes.string,
};
UnconnectedReservationPage = injectT(UnconnectedReservationPage); // eslint-disable-line

Expand Down
2 changes: 2 additions & 0 deletions app/pages/reservation/ReservationPage.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('pages/reservation/ReservationPage', () => {
unit: Immutable(Unit.build()),
user: Immutable(User.build()),
uniqueCustomerGroups: [],
reservationType: constants.RESERVATION_TYPE.NORMAL_VALUE,
};

function getWrapper(extraProps) {
Expand Down Expand Up @@ -255,6 +256,7 @@ describe('pages/reservation/ReservationPage', () => {
expect(reservationInformation.prop('openResourceTermsModal')).toBe(defaultProps.actions.openResourceTermsModal);
expect(reservationInformation.prop('order')).toBe(instance.state.order);
expect(reservationInformation.prop('reservation')).toBe(defaultProps.reservationToEdit);
expect(reservationInformation.prop('reservationType')).toBe(defaultProps.reservationType);
expect(reservationInformation.prop('resource')).toBe(defaultProps.resource);
expect(reservationInformation.prop('selectedTime')).toBeDefined();
expect(reservationInformation.prop('unit')).toBe(defaultProps.unit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class ReservationInformation extends Component {
t: PropTypes.func.isRequired,
unit: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
reservationType: PropTypes.string,
};

onConfirm = (values) => {
Expand Down Expand Up @@ -68,6 +69,7 @@ class ReservationInformation extends Component {

if (isAdmin) {
formFields.push('comments');
formFields.push('type');

/* waiting for backend implementation */
// formFields.push('reserverName');
Expand Down Expand Up @@ -132,6 +134,7 @@ class ReservationInformation extends Component {
}
if (!reservation) {
rv = this.getFormInitialValuesFromUser();
rv = { ...rv, type: constants.RESERVATION_TYPE.NORMAL_VALUE };
}
return rv;
}
Expand All @@ -148,7 +151,11 @@ class ReservationInformation extends Component {
}

getRequiredFormFields(
resource, termsAndConditions, paymentTermsAndConditions, hasPayments = false) {
resource, termsAndConditions, paymentTermsAndConditions, hasPayments = false, reservationType) {
if (reservationType === constants.RESERVATION_TYPE.BLOCKED_VALUE) {
return [];
}

const requiredFormFields = [...resource.requiredReservationExtraFields.map(
field => camelCase(field)
)];
Expand Down Expand Up @@ -199,6 +206,7 @@ class ReservationInformation extends Component {
t,
unit,
user,
reservationType,
} = this.props;

const termsAndConditions = getTermsAndConditions(resource);
Expand All @@ -225,7 +233,9 @@ class ReservationInformation extends Component {
openResourceTermsModal={openResourceTermsModal}
paymentTermsAndConditions={paymentTermsAndConditions}
requiredFields={this.getRequiredFormFields(
resource, termsAndConditions, paymentTermsAndConditions, hasPayment(order))}
resource, termsAndConditions, paymentTermsAndConditions,
hasPayment(order), reservationType)}
reservationType={reservationType}
resource={resource}
termsAndConditions={termsAndConditions}
user={user}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ describe('pages/reservation/reservation-information/ReservationInformation', ()
},
unit: Immutable(Unit.build()),
user: Immutable(User.build()),
reservationType: constants.RESERVATION_TYPE.NORMAL_VALUE,
};

function getWrapper(extraProps) {
Expand Down Expand Up @@ -80,6 +81,8 @@ describe('pages/reservation/reservation-information/ReservationInformation', ()
expect(form.prop('openResourcePaymentTermsModal')).toBe(defaultProps.openResourcePaymentTermsModal);
expect(form.prop('openResourceTermsModal')).toBe(defaultProps.openResourceTermsModal);
expect(form.prop('paymentTermsAndConditions')).toBe(getPaymentTermsAndConditions(resource));
expect(form.prop('requiredFields')).toBeDefined();
expect(form.prop('reservationType')).toBe(defaultProps.reservationType);
expect(form.prop('resource')).toBe(resource);
expect(form.prop('user')).toBe(defaultProps.user);
});
Expand Down Expand Up @@ -161,7 +164,7 @@ describe('pages/reservation/reservation-information/ReservationInformation', ()
const actual = instance.getFormFields();
// const adminFields = ['comments',
// 'reserverName', 'reserverEmailAddress', 'reserverPhoneNumber'];
const adminFields = ['comments'];
const adminFields = ['comments', 'type'];

expect(actual).toEqual([...supportedFields, ...adminFields]);
}
Expand Down Expand Up @@ -278,7 +281,7 @@ describe('pages/reservation/reservation-information/ReservationInformation', ()
}
);

test('returns InitialValues from user when no reservation', () => {
test('returns correct initial values when there is no reservation', () => {
const user = User.build({
displayName: 'First Last',
email: '[email protected]',
Expand All @@ -288,7 +291,8 @@ describe('pages/reservation/reservation-information/ReservationInformation', ()
const actual = instance.getFormInitialValues();
const expectedInfo = {
reserverName: 'First Last',
reserverEmailAddress: '[email protected]'
reserverEmailAddress: '[email protected]',
type: constants.RESERVATION_TYPE.NORMAL_VALUE,
};
expect(instance.props.reservation).toBe(null);
expect(actual).toEqual(expectedInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import ReservationSubmitButton from './ReservationSubmitButton';
import ReservationValidationErrors from './ReservationValidationErrors';
import { FIELDS } from '../../../constants/ReservationConstants';
import { isValidPhoneNumber } from '../../../utils/phoneValidationUtil';
import RadioGroup from '../../../shared/form-fields/RadioGroup';
import SingleReservationDetail from '../reservation-details/SingleReservationDetail';

const validators = {
reserverEmailAddress: (t, { reserverEmailAddress }) => {
Expand Down Expand Up @@ -312,15 +314,50 @@ class UnconnectedReservationInformationForm extends Component {
t,
termsAndConditions,
paymentTermsAndConditions,
reservationType,
} = this.props;

this.requiredFields = staffEventSelected
? constants.REQUIRED_STAFF_EVENT_FIELDS
: requiredFields;

const showEditingReservationType = includes(this.props.fields, 'type') && isEditing;
const showInputReservationType = includes(this.props.fields, 'type') && !isEditing;
return (
<div>
<Form className="reservation-form" horizontal onSubmit={handleSubmit(onConfirm)}>
{ showEditingReservationType && (
<Well>
<SingleReservationDetail
label={t('ReservationType.label')}
value={reservationType === constants.RESERVATION_TYPE.BLOCKED_VALUE
? t(constants.RESERVATION_TYPE.BLOCKED_LABEL_ID)
: t(constants.RESERVATION_TYPE.NORMAL_LABEL_ID)}
/>
</Well>
)}
{ showInputReservationType && (
<Well>
<RadioGroup
legend={t(constants.RESERVATION_TYPE.LEGEND_TEXT_ID)}
legendHint={t(constants.RESERVATION_TYPE.LEGEND_HINT_TEXT_ID)}
radioOptions={[
{
name: constants.RESERVATION_TYPE.TYPE_NAME,
value: constants.RESERVATION_TYPE.NORMAL_VALUE,
label: t(constants.RESERVATION_TYPE.NORMAL_LABEL_ID),
hint: t(constants.RESERVATION_TYPE.NORMAL_HINT_TEXT_ID),
},
{
name: constants.RESERVATION_TYPE.TYPE_NAME,
value: constants.RESERVATION_TYPE.BLOCKED_VALUE,
label: t(constants.RESERVATION_TYPE.BLOCKED_LABEL_ID),
hint: t(constants.RESERVATION_TYPE.BLOCKED_HINT_TEXT_ID),
}
]}
/>
</Well>
)}
{ includes(this.props.fields, 'reserverName') && (
<h3 className="reservationers-Info">{t('ReservationInformationForm.reserverInformationTitle')}</h3>
)}
Expand Down Expand Up @@ -681,6 +718,7 @@ UnconnectedReservationInformationForm.propTypes = {
t: PropTypes.func.isRequired,
termsAndConditions: PropTypes.string.isRequired,
paymentTermsAndConditions: PropTypes.string.isRequired,
reservationType: PropTypes.string,
};
UnconnectedReservationInformationForm = injectT(UnconnectedReservationInformationForm); // eslint-disable-line

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { hasProducts } from 'utils/reservationUtils';
import ReservationValidationErrors from './ReservationValidationErrors';
import { FIELDS } from '../../../constants/ReservationConstants';
import FormTypes from 'constants/FormTypes';
import SingleReservationDetail from '../reservation-details/SingleReservationDetail';


describe('pages/reservation/reservation-information/ReservationInformationForm', () => {
Expand Down Expand Up @@ -263,7 +264,8 @@ describe('pages/reservation/reservation-information/ReservationInformationForm',
resource: Resource.build(),
paymentTermsAndConditions: '',
termsAndConditions: '',
universalField: []
universalField: [],
reservationType: constants.RESERVATION_TYPE.NORMAL_VALUE,
};

function getWrapper(extraProps) {
Expand All @@ -281,6 +283,52 @@ describe('pages/reservation/reservation-information/ReservationInformationForm',
const reserverNameField = 'reserverName';
const eventSubjectField = 'eventSubject';

describe('reservation type', () => {
test('when type field is included and editing', () => {
const fields = ['type'];
const fieldDetail = getWrapper({ fields, isEditing: true }).find(SingleReservationDetail);
expect(fieldDetail).toHaveLength(1);
expect(fieldDetail.prop('label')).toBe('ReservationType.label');
expect(fieldDetail.prop('value')).toBe('ReservationType.normal');
});

test('when type field is not included and editing', () => {
const fields = [];
const fieldDetail = getWrapper({ fields, isEditing: true }).find(SingleReservationDetail);
expect(fieldDetail).toHaveLength(0);
});

test('when type field is included and not editing', () => {
const { RESERVATION_TYPE } = constants;
const fields = ['type'];
const radioGroup = getWrapper({ fields, isEditing: false })
.find('RadioGroup');
expect(radioGroup).toHaveLength(1);
expect(radioGroup.prop('legend')).toBe(RESERVATION_TYPE.LEGEND_TEXT_ID);
expect(radioGroup.prop('legendHint')).toBe(RESERVATION_TYPE.LEGEND_HINT_TEXT_ID);
expect(radioGroup.prop('radioOptions')).toEqual([
{
name: RESERVATION_TYPE.TYPE_NAME,
value: RESERVATION_TYPE.NORMAL_VALUE,
label: constants.RESERVATION_TYPE.NORMAL_LABEL_ID,
hint: constants.RESERVATION_TYPE.NORMAL_HINT_TEXT_ID,
},
{
name: RESERVATION_TYPE.TYPE_NAME,
value: RESERVATION_TYPE.BLOCKED_VALUE,
label: constants.RESERVATION_TYPE.BLOCKED_LABEL_ID,
hint: constants.RESERVATION_TYPE.BLOCKED_HINT_TEXT_ID,
}
]);
});

test('when type field is not included and not editing', () => {
const fields = [];
const radioGroup = getWrapper({ fields, isEditing: false }).find('RadioGroup');
expect(radioGroup).toHaveLength(0);
});
});

test('renders Reservation Information Form title', () => {
const fields = [reserverNameField];
const header = getWrapper({ fields }).find('.reservationers-Info');
Expand Down
7 changes: 7 additions & 0 deletions app/pages/reservation/reservationPageSelector.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@

import { createSelector, createStructuredSelector } from 'reselect';
import { formValueSelector } from 'redux-form';
import first from 'lodash/first';
import isEmpty from 'lodash/isEmpty';
import orderBy from 'lodash/orderBy';
import queryString from 'query-string';

import ActionTypes from 'constants/ActionTypes';
import FormTypes from 'constants/FormTypes';
import {
createIsStaffSelector,
currentUserSelector,
Expand Down Expand Up @@ -56,6 +58,10 @@ const uniqueCustomerGroupsSelector = createSelector(
}
);

const reservationTypeSelector = state => (
formValueSelector(FormTypes.RESERVATION)(state, 'type')
);

const reservationPageSelector = createStructuredSelector({
contrast: contrastSelector,
currentLanguage: currentLanguageSelector,
Expand All @@ -76,6 +82,7 @@ const reservationPageSelector = createStructuredSelector({
unit: unitSelector,
uniqueCustomerGroups: uniqueCustomerGroupsSelector,
user: currentUserSelector,
reservationType: reservationTypeSelector,
});

export default reservationPageSelector;
11 changes: 11 additions & 0 deletions app/pages/reservation/reservationPageSelector.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ function getState(resources = [], units = [], user = defaultUser) {
toShowEdited: [defaultReservation],
},
}),
form: {
RESERVATION: { values: { type: 'normal' } }
}
};
}

Expand Down Expand Up @@ -199,4 +202,12 @@ describe('pages/reservation/reservationPageSelector', () => {

expect(selected.user).toBeDefined();
});

test('returns reservationType', () => {
const state = getState();
const props = getProps();
const selected = reservationPageSelector(state, props);

expect(selected.reservationType).toBeDefined();
});
});
Loading
Loading