Complex Form Validator - salimakhtar92/ReactJs GitHub Wiki

import validator from 'validator';
import {
  OFFICE,
  PERMANENT,
  PRESENT,
  prefix,
  cities,
  pincodeDisableState,
  COMPANY
} from '../constants/appConstants';
import {objectKeyMapper} from '../utils';

class FormValidator {
  constructor(validations) {
    // validations is an array of rules specific to a form
    this.validations = validations;
    this.session = {
      current: null,
      validation: []
    };
  }

  get validation() {
    return this.validations;
  }

  set validation(validations) {
    this.validations = validations;
  }

  removeValidation(id) {
    const indexToRemove = this.validations.findIndex(({field}) => field === id);
    if (indexToRemove !== -1) {
      this.validations.splice(indexToRemove, 1);
    }
  }

  validate(state, flag) {
    let mutatedState = null;
    let listOfRules = [];
    if (Object.keys(state).length > 2) {
      mutatedState = {
        ...state,
        ...objectKeyMapper(
          state[PERMANENT],
          {affixType: prefix, operator: '-', value: PERMANENT},
          [cities, pincodeDisableState]
        ),
        ...objectKeyMapper(
          state[OFFICE],
          {affixType: prefix, operator: '-', value: OFFICE},
          [cities, pincodeDisableState]
        ),
        ...objectKeyMapper(
          state[PRESENT],
          {affixType: prefix, operator: '-', value: PRESENT},
          [cities, pincodeDisableState]
        ),
        ...objectKeyMapper(
          state[COMPANY],
          {affixType: prefix, operator: '-', value: COMPANY},
          [cities, pincodeDisableState]
        ),
        ...objectKeyMapper(
          state.spouseName,
          {affixType: prefix, operator: '-', value: 'spouse'}
        ),
        ...objectKeyMapper(
          state.fatherName,
          {affixType: prefix, operator: '-', value: 'father'},
        ),
        ...objectKeyMapper(
          state.motherName,
          {affixType: prefix, operator: '-', value: 'mother'},
        ),
        ...objectKeyMapper(
          state.firstReference,
          {affixType: prefix, operator: '-', value: 'firstReference'},
        ),
        ...objectKeyMapper(
          state.secondReference,
          {affixType: prefix, operator: '-', value: 'secondReference'},
        ),
        ...state.carDetails,
        ...state.name,
        ...state.creditDetailsDto
      };
      if (state.coBorrower) {
        const {
          name: coBorrowerName,
          dob: coBorrowerDOB,
          contactNumber: coBorrowerContactNumber,
          gender: coBorrowerGender,
          monthlyIncome: coBorrowerMonthlyIncome,
          existingEmi: coBorrowerExistingEmi,
          permanentAddress: coBorrowerPermanentAddress,
          presentAddress: coBorrowerPresentAddress,
          panNo: coBorrowerPanNo,
          firstReference: coBorrowerFirstReference,
          secondReference: coBorrowerSecondReference
        } = state.coBorrower;
        mutatedState = {
          ...mutatedState,
          ...objectKeyMapper(
            coBorrowerName,
            {affixType: prefix, operator: '-', value: 'coBorrower'}
          ),
          ['coBorrower-dob']: coBorrowerDOB,
          ['coBorrower-contactNumber']: coBorrowerContactNumber,
          ['coBorrower-panNo']: coBorrowerPanNo,
          ['coBorrower-monthlyIncome']: coBorrowerMonthlyIncome,
          ['coBorrower-existingEmi']: coBorrowerExistingEmi,
          ['coBorrower-gender']: coBorrowerGender,
          ['coBorrower-Employment Details-635']: state.coBorrower['Employment Details-635'],
          ['coBorrower-Employment Type-636']: state.coBorrower['Employment Type-636'],
          ['coBorrower-Accommodation Status-43']: state.coBorrower['Accommodation Status-43'],
          ['coBorrower-Occupation-640']: state.coBorrower['Occupation-640'],
          ['coBorrower-Relationship-641']: state.coBorrower['Relationship-641'],
          ...objectKeyMapper(
            coBorrowerFirstReference,
            {affixType: prefix, operator: '-', value: 'coBorrower-firstReference'},
          ),
          ...objectKeyMapper(
            coBorrowerSecondReference,
            {affixType: prefix, operator: '-', value: 'coBorrower-secondReference'},
          ),
          ...objectKeyMapper(
            coBorrowerPermanentAddress,
            {affixType: prefix, operator: '-', value: `${PERMANENT}-coBorrower`},
            [cities, pincodeDisableState, 'region', 'locality']
          ),
          ...objectKeyMapper(
            coBorrowerPresentAddress,
            {affixType: prefix, operator: '-', value: `${PRESENT}-coBorrower`},
            [cities, pincodeDisableState, 'region', 'locality']
          )
        };
      }

      if (state.coGuarantor) {
        const {
          name: coGuarantorName,
          dob: coGuarantorDOB,
          contactNumber: coGuarantorContactNumber,
          gender: coGuarantorGender,
          monthlyIncome: coGuarantorMonthlyIncome,
          existingEmi: coGuarantorExistingEmi,
          permanentAddress: coGuarantorPermanentAddress,
          presentAddress: coGuarantorPresentAddress,
          panNo: coGuarantorPanNo,
          firstReference: coGuarantorFirstReference,
          secondReference: coGuarantorSecondReference
        } = state.coGuarantor;
        mutatedState = {
          ...mutatedState,
          ...objectKeyMapper(
            coGuarantorName,
            {affixType: prefix, operator: '-', value: 'coGuarantor'}
          ),
          ['coGuarantor-dob']: coGuarantorDOB,
          ['coGuarantor-contactNumber']: coGuarantorContactNumber,
          ['coGuarantor-panNo']: coGuarantorPanNo,
          ['coGuarantor-monthlyIncome']: coGuarantorMonthlyIncome,
          ['coGuarantor-existingEmi']: coGuarantorExistingEmi,
          ['coGuarantor-gender']: coGuarantorGender,
          ['coGuarantor-Employment Details-639']: state.coGuarantor['Employment Details-639'],
          ['coGuarantor-Employment Type-638']: state.coGuarantor['Employment Type-638'],
          ['coGuarantor-Occupation-642']: state.coGuarantor['Occupation-642'],
          ['coGuarantor-Relationship-643']: state.coGuarantor['Relationship-643'],
          ...objectKeyMapper(
            coGuarantorFirstReference,
            {affixType: prefix, operator: '-', value: 'coGuarantor-firstReference'},
          ),
          ...objectKeyMapper(
            coGuarantorSecondReference,
            {affixType: prefix, operator: '-', value: 'coGuarantor-secondReference'},
          ),
          ...objectKeyMapper(
            coGuarantorPermanentAddress,
            {affixType: prefix, operator: '-', value: `${PERMANENT}-coGuarantor`},
            [cities, pincodeDisableState, 'region', 'locality']
          ),
          ...objectKeyMapper(
            coGuarantorPresentAddress,
            {affixType: prefix, operator: '-', value: `${PRESENT}-coGuarantor`},
            [cities, pincodeDisableState, 'region', 'locality']
          )
        };
      }

      delete mutatedState[PRESENT];
      delete mutatedState[PERMANENT];
      delete mutatedState[OFFICE];
      delete mutatedState[COMPANY];
      delete mutatedState.firstReference;
      delete mutatedState.secondReference;
      delete mutatedState.coBorrower;
      delete mutatedState.coGuarantor;
      delete mutatedState.spouseName;
      delete mutatedState.fatherName;
      delete mutatedState.motherName;
      delete mutatedState.name;
      delete mutatedState.carDetails;

      if (state.flagCheck) {
        listOfRules = [...this.validations.filter(
          ({type}) => {
            return !!(type && type === 'coborrower'); // Need to check for co-guarantor
          })];
      } else {
        listOfRules = this.validations;
      }
    } else {
      mutatedState = state;
      const key = Object.keys(mutatedState)[0];
      if (this.session.current !== key) {
        this.session.current = key;
        this.session.validation = [...this.validations.filter(
          ({field, validateWhenTyping = true}) => {
            return !!(validateWhenTyping && field === key);
          })];
      }
      listOfRules = this.session.validation;
    }
    return this.applyValidations(mutatedState, listOfRules, flag);
  }

  applyValidations(mutatedState, listOfRules, flag) {
    // start out assuming valid
    let validation = this.valid(listOfRules);
    // for each validation rule
    listOfRules.forEach(rule => {
      // if the field isn't already marked invalid by an earlier rule
      if (!validation[rule.field].isInvalid) {
        // determine the field value, the method to invoke and
        // optional args from the rule definition
        let field_value = mutatedState[rule.field];
        if (flag !== 3) {
          field_value = mutatedState[rule.field] || mutatedState[rule.field] === 0
            ? mutatedState[rule.field].toString() :
            '';
        }
        const args = rule.args || ['en-IN'](/salimakhtar92/ReactJs/wiki/'en-IN');
        const validation_method = typeof rule.method === 'string' ?
          validator[rule.method] :
          rule.method;
        // call the validation_method with the current field value
        // as the first argument, any additional arguments, and the
        // whole state as a final argument.  If the result doesn't
        // match the rule.validWhen property, then modify the
        // validation object for the field and set the isValid
        // field to false
        if (validation_method(field_value, ...args, mutatedState) !== rule.validWhen) {
          validation = this.Invalid(validation, rule);
        }
      }
    });
    return validation;
  }

  Invalid(validation, rule) {
    validation[rule.field] = {
      isInvalid: true,
      message: rule.message
    };
    validation.isValid = false;
    return validation;
  }
  // create a validation object for a valid form
  valid(rules) {
    const validation = {};

    rules.map(rule => (
      validation[rule.field] = {isInvalid: false, message: ''}
    ));
    return {isValid: true, ...validation};
  }
}

export default FormValidator;