containers/Betslip/index.js


import React from 'react';
import { connect } from 'react-redux';

import Constants from '../../constants';
import { parseNum, roundNum } from '../../utils/parser';
import {
  placebetRequest, toggleSelectedOdds, openModal, setStakes, toggleBetslip,
  closePlaceBetErrorMessage, closeSlipConfirmation,
  setSelectedBetType, setBackUpLoginProps, closeBetSlip, clearAllSelections,
} from '../../reducers';
import {
  getMarkets,
  getMatches,
  getGameType,
  getWinCap,
  getRoundId,
} from '../../store/bets';
import {
  getBetslipSettings, getConfigs, getFreeBets,
  getUser,
  getSelectedBetType,
  getPlacebetBackupLogin,
  getPrevCombinationTypes,
  getPrevCombinations,
} from '../../store/common';
import {
  getNumberOfSelections, getSelectedOdds, getSingleStakes, getMultipleStake, getOtherStake,
  getCombiStakes, getPlaceBetErrorMgs, getIsLoadingPlaceBet, getBetSlipConfimation,
  getPlacebetDisabled, getBonusPercentages, getBonusOddsThreshold, getTurboRoundDuration,
  getPlacebetAfterLogin,
} from '../../store/placebet';
import BetslipView from '../../components/Betslip/BetslipView';
import BetslipHandler from './BetslipHandler';

const mapToProps = (state) => ({
  markets: getMarkets(state),
  numberOfSelections: getNumberOfSelections(state),
  betslipSettings: getBetslipSettings(state),
  singleStakes: getSingleStakes(state),
  multipleStake: getMultipleStake(state),
  otherStake: getOtherStake(state),
  combiStakes: getCombiStakes(state),
  matches: getMatches(state),
  placeBetErrorMgs: getPlaceBetErrorMgs(state),
  isLoadingPlaceBet: getIsLoadingPlaceBet(state),
  betSlipConfimation: getBetSlipConfimation(state),
  placebetDisabled: getPlacebetDisabled(state),
  bonusPercentages: getBonusPercentages(state),
  bonusOddsThreshold: getBonusOddsThreshold(state),
  config: getConfigs(state),
  odds: getSelectedOdds(state),
  freeBets: getFreeBets(state),
  gameType: getGameType(state),
  turboRoundDuration: getTurboRoundDuration(state),
  selectedBetType: getSelectedBetType(state),
  user: getUser(state),
  placebetBackupLogin: getPlacebetBackupLogin(state),
  prevCombinationTypes: getPrevCombinationTypes(state),
  prevCombinations: getPrevCombinations(state),
  selectedOdds: getSelectedOdds(state),
  winCap: getWinCap(state),
  placebetAfterLogin: getPlacebetAfterLogin(state),
  roundId: getRoundId(state),
});

const actionsToProps = (dispatch) => ({
  placebetRequest: (payload) => dispatch(placebetRequest(payload)),
  toggleSelectedOdds: (payload) => dispatch(toggleSelectedOdds(payload)),
  openModal: (payload) => dispatch(openModal(payload)),
  setStakes: (payload) => dispatch(setStakes(payload)),
  toggleBetslip: (payload) => dispatch(toggleBetslip(payload)),
  closePlaceBetErrorMessage: (payload) => dispatch(closePlaceBetErrorMessage(payload)),
  closeSlipConfirmation: (payload) => dispatch(closeSlipConfirmation(payload)),
  setSelectedBetType: (payload) => dispatch(setSelectedBetType(payload)),
  setBackUpLoginProps: (payload) => dispatch(setBackUpLoginProps(payload)),
  closeBetSlip: (payload) => dispatch(closeBetSlip(payload)),
  clearAllSelections: (payload) => dispatch(clearAllSelections(payload)),
});

/**
 * @class
 * @augments BetslipHandler
 * @property {object} props
 * @property {Array} props.markets All markets data
 * @property {number} props.numberOfSelections Number of selected odds
 * @property {object} props.betslipSettings Config info from API
 * @property {Array} props.singleStakes Amounts in inputs on single betslip type
 * @property {string} props.multipleStake Amount in input on multiple betslip type
 * @property {string} props.otherStake Amount in input on split betslip type or free bets bet type
 * @property {Array} props.combiStakes Amounts in inputs on combination betslip type
 * @property {Array} props.matches Matches informations
 * @property {string} props.placeBetErrorMgs Error message from API
 * @property {boolean} props.isLoadingPlaceBet Represent is placebet API fetch
 * @property {boolean} props.betSlipConfimation Indicate when confirmation modal sholud show
 * @property {boolean} props.placebetDisabled Indicate when placebet button should be disabled
 * @property {number} props.bonusPercentages Config form API
 * @property {string} props.bonusOddsThreshold Config form API
 * @property {object} props.config Contain currency sign
 * @property {object} props.odds Contain all odds
 * @property {Array} props.freeBets Free bets information
 * @property {Array} props.gameType FGame type Classic/Turbo
 *
 * @property {Function} props.placebet Placebet API call
 * @property {Function} props.toggleSelectedOdds Toggle selected odds
 * @property {Function} props.openModal Open modal
 * @property {Function} props.setStakes Change stakes in betslip
 * @property {Function} props.toggleBetslip Toggle betslip modal
 * @property {Function} props.closePlaceBetErrorMessage Remove placebet error message
 * @property {Function} props.closeSlipConfirmation Remove placebet confirmation modal
 * @property {Function} props.kickOff Get turbo live data
 */
class Betslip extends BetslipHandler {
  constructor(props) {
    super(props);
    /**
     * @member {object}
     * @description Initial setup for calculations
     * @property {number} [totalStake=0]
     * @property {number} [potWinMax=0]
     * @property {number} [potWinMin=0]
     * @property {number} [bonusAmt=0]
     */
    this.setup = {
      totalStake: 0,
      potWinMax: 0,
      potWinMin: 0,
      bonusAmt: 0,
    };
    /**
     * @member {object}
     * @property {string} type Indicate one of bet type
     * @property {number} numOfSplitBets Number of split bets
     * @property {Array} maxOdd Array of all maximum odds per combination.
     *  For split bet only one odd
     * @property {Array} minOdd Array of all minimum odds per combination.
     *  For split bet only one odd
     * @property {Array} maxPotWin Array of all maximum potential win per combination.
     *  For split bet only one odd
     * @property {Array} minPotWin Array of all minimum potential win combination.
     *  For split bet only one odd
     * @property {boolean} minStakeErr Is all inserted stakes bigger than minimum stake
     * @property {boolean} maxStakeErr Is all inserted stakes less than minimum stake
     * @property {number} stakeInFocus Index of input field in focus
     * @property {boolean} rerender Forse rerender
     * @property {boolean} showButtons Indicate are stake buttons showen
     * @property {boolean} openNewPlacedBetsModal Indicate is confirmation modal showen
     * @property {object} systemManager Precalculated data from library
     * @property {object} settings Settings for calculations
     */
    this.state = {
      type: Constants.BET_TYPE.NO_BET,
      selectedType: Constants.BET_TYPE.NO_BET,
      numOfSplitBets: 0,
      maxOdd: [],
      minOdd: [],
      maxPotWin: [],
      minPotWin: [],
      minStakeErr: false,
      maxStakeErr: false,
      maxPotWinPayoutErr: false,
      stakeInFocus: 0,
      rerender: false,
      showButtons: false,
      openNewPlacedBetsModal: false,
      systemManager: {},
      settings: this.setup,
      stakeHasChanges: false,
      ticker: 0,
    };

    /**
     * @member {Array<string>}
     * @description Array of combination inputs label
     */
    this.combinationTypes = [];
    /**
     * @member {Array<string>}
     * @description Array of number of bets per combination
     */
    this.combinations = [];

    this.tickerInterval = setInterval(this.checkCalc, 1000);
  }

  /**
   * Calculate bet type on component mount
   *
   * @returns {void}
   */
  componentDidMount() {
    this.changeBetType(this.props.selectedBetType);
  }

  /**
   * Setup settings when selected type change, calculate bet type on odd remove
   *
   * @param {object} prevProps
   * @returns {void}
   */
  componentDidUpdate(prevProps) {
    if (this.props.selectedBetType !== prevProps.selectedBetType) {
      // eslint-disable-next-line react/no-did-update-set-state
      this.setState({
        settings: this.setupPlacebet(
          this.props.singleStakes,
          this.props.combiStakes,
          this.props.multipleStake,
          this.props.otherStake,
        ),
        stakeInFocus: 0,
        showButtons: false,
      });
      this.changeBetType(this.props.selectedBetType);
      if (this.state.minStakeErr === true
        || this.state.maxStakeErr === true
        || this.state.maxPotWinPayoutErr === true
        || this.props.placeBetErrorMgs) {
        this.closeError();
      }
    }
    if (this.props.numberOfSelections !== prevProps.numberOfSelections) {
      this.changeBetType(
        this.props.selectedBetType,
        this.props.numberOfSelections < prevProps.numberOfSelections,
      );
    }
  }

  /**
   * Open modal view placed bets on component unmount if bet is placed
   *
   * @returns {void}
   */
  componentWillUnmount() {
    this.isUnmounted = true;
    if (this.props.betSlipConfimation || this.state.openNewPlacedBetsModal) {
      this.props.openModal({
        modal: Constants.MODALS.VIEW_PLACED_BETS,
      });
    }
    this.closeError();
    this.clearAllTimeouts();
    clearInterval(this.tickerInterval);
  }

  thereIsStake = () => this.props.multipleStake > 0 || this.props.singleStakes.length > 0
    || this.props.combiStakes.length > 0;

  thereAreSelections = () => this.props.selectedOdds.length > 0;

  getBetType = () => {
    if (this.props.selectedOdds.length === 1) {
      return Constants.BET_TYPE.SINGLE;
    }

    const matches = {};
    this.props.selectedOdds.map((selection) => {
      const matchId = selection.matchId;
      matches[matchId] = true;
    });

    if (Object.keys(matches).length < this.props.selectedOdds.length) {
      return Constants.BET_TYPE.SPLIT_COLUMN;
    }

    return this.props.combiStakes.length > 0
      ? Constants.BET_TYPE.COMBINATION : Constants.BET_TYPE.MULTIPLE;
  }

  checkCalc = () => {
    if (this.state.selectedType === Constants.BET_TYPE.NO_BET
      && this.thereIsStake() && this.thereAreSelections()) {
      this.updateCalc();
      this.setState({ selectedType: this.props.selectedBetType });
    }
  }

  /**
   * Render
   *
   * @see module:Betslip/BetslipView
   * @returns {view}
   */
  render() {
    const selectedBetType = this.props.selectedBetType;
    const combinationTypes = this.props.prevCombinationTypes || this.combinationTypes;
    const combinations = this.props.prevCombinations || this.combinations;
    const potWinMin = roundNum(this.state.settings.potWinMin);
    const potWinMax = roundNum(this.state.settings.potWinMax);
    const potWin = potWinMax > 0
      ? potWinMin === potWinMax
        ? parseNum(potWinMax * 100) : `${parseNum(potWinMin * 100)} to ${parseNum(potWinMax * 100)}`
      : 0;
    const placeBetErrorMgs = this.state.minStakeErr
      ? (this.props.selectedBetType === Constants.BET_TYPE.COMBINATION
        || this.props.selectedBetType === Constants.BET_TYPE.SPLIT_COLUMN)
        ? `Minimum stake per combination is: ${parseNum(this.props.betslipSettings.minStakePerSelection * 100)} ${this.props.config.currencySymbol}`
        : `Minimum stake is: ${parseNum(this.props.betslipSettings.minStake * 100)} ${this.props.config.currencySymbol}`
      : this.state.maxStakeErr
        ? `Total Stake Cannot Be Higher Than: ${parseNum(this.props.betslipSettings.maxStake * 100)} ${this.props.config.currencySymbol}`
        : this.props.placeBetErrorMgs;

    return (
      <BetslipView
        {...this.props}
        {...this.state}
        combinationTypes={combinationTypes}
        combinations={combinations}
        potWin={potWin}
        totalStake={this.state.settings.totalStake}
        placeBetErrorMgs={placeBetErrorMgs}
        bonusAmt={this.state.settings.bonusAmt}
        percentage={this.state.settings.percentage}
        getFreeBetsAmount={this.getFreeBetsAmount}
        placebet={this.placebet}
        changeStakeInput={this.changeStakeInput}
        addButton={this.addButton}
        freeBets={this.props.freeBets}
        openOtherTypes={this.openOtherTypes}
        openFreeBetType={this.openFreeBetType}
        selectSingleBet={this.selectSingleBet}
        selectMultipleBet={this.selectMultipleBet}
        setInputInFocus={this.setInputInFocus}
        setInputOutOfFocus={this.setInputOutOfFocus}
        clearAllSelections={this.clearAllSelections}
        closeConfirmation={this.closeSlipConfirmation}
        clearSelection={this.clearSelection}
        closeError={this.closeError}
        toggleBetslipModal={this.toggleBetslipModal}
        clearStake={this.clearStake}
        selectedBetType={selectedBetType}
        haveLiveMatches={this.props.haveLiveMatches}
        isWinCap={this.state.maxPotWinPayoutErr}
        winCap={this.props.winCap}
        toggleBetslip={this.toggleBetslipModal}
      />
    );
  }
}
export default connect(
  mapToProps,
  actionsToProps,
)(Betslip);