import Pages from '../data/enum/Pages';
import matchRoute from '../util/routeCheckUtils';
import { measurementFields, membershipCardFields } from '../data/enum/FieldNames/AccountFieldNames';
import { clearValidation } from '../enhanced-redux-form/actions/enhancedFormActions';
import * as ValidateOn from '../enhanced-redux-form/data/ValidateOn';
import { required, minLength, maxLength } from '../util/form/basic-validations';
import { userProfileSelector } from '../selectors/userProfileSelectors';
import { isBmiRangeSafe, isTransferBmiRangeSafe, isDangerous, isAtRisk } from '../util/BmiUtils';
import { validateWeight, updateWeight } from '../actions/resources/weighInActions';
import {
ERROR_BMI_TOO_LOW,
ERROR_BMI_NOT_ALLOWED,
ERROR_BMI_DANGEROUS,
} from '../data/validationErrorCodes';
import WeightHeightRanges from '../data/enum/WeightHeightRanges';
import MembershipCard from '../data/enum/MembershipCard';
/*
* Simple helper function to create a 'required' validation rule, to be used in conjuntion
* with other validation rules for a given field
*/
export const createRequiredRule = (fieldName, formName = 'validation') => ({
rule: required,
message: { locale: `${formName}.errors.${fieldName}.required` },
});
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const regexTester = regex => value => regex.test(value);
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const getHeight = () => (dispatch, getState) => {
const profile = userProfileSelector(getState());
return profile && profile.height;
};
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const getSupplimentaryHeight = () => (dispatch, getState) =>
getState().supplementary.profileHeight;
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const bmiLowRule = (form, fieldName, isGroupMember) => ({
rule: (value, values, _, dispatch) => {
const groupRoutes = [Pages.GR_MEDICAL_CHECK, Pages.NEW_GROUP_JOURNEY_TARGET_WEIGHT];
const matchRoutes = () => (__, getState) =>
groupRoutes.map(route => matchRoute(route, getState())).filter(x => x).length > 0;
// Do we match on the group routes?
const isGroupFlow = isGroupMember || dispatch(matchRoutes());
!isGroupFlow && dispatch(clearValidation(form, [fieldName, measurementFields.HEIGHT]));
const height = values[measurementFields.HEIGHT] || dispatch(getHeight()) || null;
if (!isGroupFlow && values && values[fieldName] && height) {
if (fieldName === measurementFields.TARGET_WEIGHT && isDangerous(values[fieldName], height)) {
// return false (because we will catch the target error in the bmiDangerousRule check)
return false;
}
return !isAtRisk(values[fieldName], height);
}
return true;
},
code: ERROR_BMI_TOO_LOW,
});
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const bmiDangerousRule = (form, fieldName) => ({
rule: (value, values, _, dispatch) => {
const groupRoutes = [Pages.GR_MEDICAL_CHECK];
const matchRoutes = () => (__, getState) =>
groupRoutes.map(route => matchRoute(route, getState())).filter(x => x).length > 0;
// Do we match on the group routes?
const isGroupFlow = dispatch(matchRoutes());
!isGroupFlow && dispatch(clearValidation(form, [fieldName, measurementFields.HEIGHT]));
const height = values[measurementFields.HEIGHT] || dispatch(getHeight()) || null;
if (!isGroupFlow && values && values[fieldName] && height) {
if (fieldName === measurementFields.TARGET_WEIGHT) {
return !isDangerous(values[fieldName], height);
}
}
return true;
},
code: ERROR_BMI_DANGEROUS,
});
// Always return true, as we dont want to block the form
// validateWeight will add server validation into the
// weighin section of state
export const weighInValidation = () => ({
rule: (value, values, _, dispatch) => {
dispatch(validateWeight(value));
return true;
},
code: ERROR_BMI_TOO_LOW,
});
export const editWeighInValidation = () => ({
rule: (value, values, _, dispatch) => {
dispatch(updateWeight(true));
return true;
},
code: ERROR_BMI_TOO_LOW,
});
/*
* Simple helper function to create a 'required' validation rule, to be used in conjunction
* with other validation rules for a given field
*/
export const bmiNotAllowedRule = (form, fieldName, isGroupMember) => ({
rule: (value, values, _, dispatch) => {
const groupRoutes = [Pages.GR_MEDICAL_CHECK];
const matchRoutes = () => (__, getState) =>
groupRoutes.map(route => matchRoute(route, getState())).filter(x => x).length > 0;
const matchTransferRoute = () => (__, getState) =>
matchRoute(Pages.TRANSFER_TO_ONLINE_BMI, getState());
// Do we match on the group routes?
const isGroupFlow = isGroupMember || dispatch(matchRoutes());
const isTransferFlow = dispatch(matchTransferRoute());
!isGroupFlow && dispatch(clearValidation(form, [fieldName, measurementFields.HEIGHT]));
const height = values[measurementFields.HEIGHT] || dispatch(getHeight());
if (isTransferFlow && values && values[fieldName] && height) {
return isTransferBmiRangeSafe(values[fieldName], height);
}
if (!isGroupFlow && values && values[fieldName] && height) {
return isBmiRangeSafe(values[fieldName], height);
}
// values are not set yet.
// the other 'required' rules will fail in this case, so we can return 'true' here
return true;
},
code: ERROR_BMI_NOT_ALLOWED,
});
/**
* Checks weight value against the defined upper range for weight
* Called from inside a validation rule
**/
export const weightUpperRange = () => ({
rule: value => {
if (value === undefined || value < WeightHeightRanges.WEIGHT_UPPER_LIMIT) {
return true;
}
return false;
},
message: { locale: 'validation.errors.initialWeight.tooHigh' },
});
/**
* Checks weight value against the defined lower range for weight
* Called from inside a validation rule
**/
export const weightLowerRange = () => ({
rule: value => {
if (value === undefined || value > WeightHeightRanges.WEIGHT_LOWER_LIMIT) {
return true;
}
return false;
},
message: { locale: 'validation.errors.initialWeight.tooLow' },
});
export const membershipCardNumberLength = cardNumberRequired => ({
rule: value => {
// pass validation when field is empty not required to stop crash
if (!cardNumberRequired && !value) {
return true;
}
const valueNoSpace = stripSpaces(value);
return (
(valueNoSpace.length === MembershipCard.CARD_MIN_LENGTH ||
valueNoSpace.length === MembershipCard.CARD_MAX_LENGTH) &&
!!Number(valueNoSpace)
);
},
message: { locale: `validation.errors.${membershipCardFields.CARD_NUMBER}.range` },
});
// Strip spaces from a string
export const stripSpaces = value => {
const spaceRegex = new RegExp(' ', 'g');
return value.replace(spaceRegex, '');
};
/*
* Simple helper function to create a complete 'required' validation config,
* when there is no other validations needed upo the field.
*/
const createSimpleRequiredValidation = (fieldName, validateOn, formName) => ({
[fieldName]: {
validators: [createRequiredRule(fieldName, formName)],
validateOn: validateOn || [ValidateOn.BLUR],
},
});
export default createSimpleRequiredValidation;
export const usernameValid = regexTester(/^[a-z0-9]([._][a-z0-9]|[a-z0-9])*$/i);
export const usernameMaxLength = value => maxLength(value, 19);
export const usernameMinLength = value => minLength(value, 3);