components/Betslip/RenderMethods.js

/**
 * @module Betslip/BetslipView
 */
import React, { Component } from 'react';
import Constants, { SVG_ICONS } from '../../constants';
import SVGComponent from '../SVG/SVGComponent';
import PlacedBetsDenined from './PlacedBetsDenined';
import { parseBigNums, parseNum } from '../../utils/parser';
import { getSelection, getTimeTextFormat } from '../../utils/common';

/**
 * Render methods for Beslip View
 *
 * @class
 */
export default class RenderMethods extends Component {
  /**
   * onKeyDown
   *
   * @function
   * @param {object} e
   * @param {number|null} stake
   * @returns {void}
   */
  onKeyDown = (e, stake) => {
    if (e.keyCode === 8 && parseNum(stake * 100) < 10) {
      this.props.changeStakeInput({ target: { value: 0 } });
    }
  }

  handlePlacebetClick = () => {
    if (this.props.isLoadingPlaceBet) {
      return;
    }
    const time = Date.now();
    if (this.lastClick === undefined || time - this.lastClick >= 1000) {
      this.lastClick = time;
      this.props.placebet();
    }
  }

  /**
   * Render inputs
   *
   * @function
   * @param {number|null} stake
   * @param {number} focusIndex
   * @param {boolean} [isFreeBet]
   * @returns {view}
   */
  renderBetStake = (stake, focusIndex, isFreeBet) => (
    <div className="betslip__bet-item stake">
      <div className="betslip__bet-stake">
        <input
          type="text"
          className={`betslip__bet-stake-input ${stake > 0 ? 'valid' : ''} ${isFreeBet && 'free-bet'}`}
          value={stake ? parseNum(stake * 100) : ''}
          onChange={this.props.changeStakeInput}
          onFocus={this.props.setInputInFocus.bind(this, focusIndex)}
          onBlur={this.props.setInputOutOfFocus}
          onKeyDown={(e) => this.onKeyDown(e, stake)}
          pattern="[0-9]*"
          inputMode="numeric"
          placeholder="0"
        />
        <div className="betslip__bet-stake-currency">{this.props.config.currencySymbol}</div>
        {isFreeBet && (
          <div className="betslip__bet-stake-gift">
            <SVGComponent className="icon-m" src={`${SVG_ICONS.utility}#icon__giftbox-g`} />
          </div>
        )}
      </div>
    </div>
  );

  /**
   * Render one selection row
   *
   * @function
   * @param {string} key
   * @param {string|null} heading
   * @param {string} odd
   * @param {number|null} stake
   * @param {number|null} focusIndex
   * @param {view} [childrens]
   * @param {boolean} [isFreeBet]
   * @returns {view}
   */
  renderSelectionRow = (key, heading, odd, stake, focusIndex, childrens, isFreeBet) => (
    <div className="betslip__bet" key={key}>
      <div className="betslip__bet-item">
        {childrens || <span className="ml-3">{heading}</span>}
      </div>
      <div className="betslip__bet-item odds">
        <div className="betslip__odds--value">{parseBigNums(odd)}</div>
      </div>
      {stake !== null && this.renderBetStake(stake, focusIndex, isFreeBet)}
    </div>
  );

  /**
   * Render match section
   *
   * @function
   * @param {number} index
   * @param {string} match
   * @param {object} selection
   * @param {boolean} [dark]
   * @returns {view}
   */
  renderMatch = (index, match, selection, dark) => (
    <>
      <div className="betslip__bet-icon">
        <SVGComponent
          className="icon-s"
          src={`${SVG_ICONS.utility}#icon__bin${dark ? '-dark' : ''}`}
          onClick={this.props.clearSelection.bind(this, index)}
        />
      </div>

      <div className="betslip__bet-match">
        <span className="betslip__bet-teams">{match}</span>
        <span className="betslip__bet-label">{selection.fullName}</span>
      </div>
    </>
  );

  /**
   * Render remove all button
   *
   * @function
   * @returns {view}
   */
  renderRemoveAll = () => (
    <div className="betslip__heading-remove" onClick={this.props.clearAllSelections}>
      <div className="betslip__bet-icon">
        <SVGComponent className="icon-s" src={`${SVG_ICONS.utility}#icon__bin`} />
      </div>
      <div className="ml-3">Remove all</div>
    </div>
  );

  /**
   * Render Close Betslip Modal Button
   *
   * @function
   * @returns {view}
   */
  renderCloseBetslip = () => (
    <div className="betslip__heading-close" onClick={this.props.toggleBetslip}>
      <SVGComponent className="icon-m" src={`${SVG_ICONS.utility}#icon__close`} />
    </div>
  );

  /**
   * Render confirmation modal
   *
   * @function
   * @returns {view}
   */
  renderBetSlipConfimation = () => (
    <div className="betslip__confirmation">
      <div className="betslip__confirmation-heading">BETS PLACED!</div>
      <div className="betslip__confirmation-label">
        Would you like to add existing
        <br />
        selections to another betslip?
      </div>
      <div className="betslip__confirmation-label d-flex">
        <button
          type="button"
          className="btn btn-danger btn-block btn-sm"
          onClick={this.props.clearAllSelections}
        >
          Clear Selections
        </button>
        <button
          type="button"
          className="btn btn-primary ml-4 mt-0 btn-block btn-sm"
          onClick={this.props.closeConfirmation}
        >
          Yes
        </button>
      </div>
    </div>
  );

  /**
   * Render confirmation modal Turbo
   *
   * @function
   * @returns {view}
   */
  renderTurboBetSlipConfimation = () => {
    const timeTemplate = `closed after ${getTimeTextFormat(this.props.turboRoundDuration)}`;
    return (
      <div className="betslip__confirmation">
        <div className="betslip__confirmation-heading">Bets placed !</div>
        <div className="betslip__confirmation-label d-flex">
          <button
            type="button"
            className="btn btn-primary btn-block btn-sm"
            id="moreBetsBtn"
            onClick={this.props.clearAllSelections}
          >
            More Bets
          </button>
          <button
            type="button"
            className="btn btn-secondary ml-4 mt-0 btn-block btn-sm"
            onClick={this.props.kickOff}
            disabled={this.props.haveLiveMatches}
          >
            Kick Off !
          </button>
        </div>
        <div className="betslip__confirmation-label">
          These events will be automatically
          <br />
          {timeTemplate}
        </div>
      </div>
    );
  };

  /**
   * Render bonus content
   *
   * @function
   * @returns {view}
   */
  renderBonusInfo = () => (
    <div className="betslip__stake-boost">
      <div className="betslip__stake-boost-label">
        {this.props.percentage ? `${this.props.percentage}% ` : ''}
        {this.props.selectedBetType}
        {' '}
        Boost
        <br />
        <strong>INCLUDED</strong>
      </div>
      <div className="betslip__stake-boost-item">
        <div className="betslip__stake-boost-amount" id="betslipBonusAmount">
          {this.props.config.currencySymbol}
          {' '}
          {parseNum(Math.min(this.props.winCap, this.props.bonusAmt * 100))}
        </div>
        <SVGComponent
          className="icon-l ml-3"
          src={`${SVG_ICONS.utility}#icon__rocket`}
          onClick={this.props.toggleBetslip}
        />
      </div>
    </div>
  );

  /**
   * Render bottom navigation
   *
   * @function
   * @returns {view}
   */
  renderBottomNav = () => (
    <div className="betslip__nav">
      <div
        id="betTypeSingle"
        className={`betslip__nav-item ${this.getSingleActivityStatus()}`}
        onClick={this.props.selectSingleBet}
      >
        {Constants.BET_TYPE.SINGLE}
      </div>
      <div
        id="betTypeMultiple"
        className={`betslip__nav-item ${this.getMultipleActivityStatus()}`}
        onClick={this.props.selectMultipleBet}
      >
        {Constants.BET_TYPE.MULTIPLE}
      </div>
      <div
        id="betTypeCombi"
        className={`betslip__nav-item ${this.getCombiActivityStatus()}`}
        onClick={this.props.openOtherTypes}
      >
        {Constants.BET_TYPE.COMBINATION}
      </div>
      {this.props.freeBets.length > 0 && (
        <div
          id="betTypeFree"
          className={`betslip__nav-item ${this.getFreeBetsActivityStatus()}`}
          onClick={this.props.openFreeBetType}
        >
          <SVGComponent className="icon-m mr-3" src={`${SVG_ICONS.utility}#icon__giftbox`} />
          <span>FREE</span>
        </div>
      )}
    </div>
  );

  /**
   * Render stake buttons
   *
   * @function
   * @returns {view}
   */
  renderChips = () => {
    if (!this.props.betSlipConfimation) {
      if (this.isFreeBet()) {
        if (!this.freeBetsAmounts) {
          this.freeBetsAmounts = this.props.getFreeBetsAmount();
        }
        return (
          <div className="betslip__free-bet">
            {this.freeBetsAmounts.map((chip) => (
              <div className="betslip__free-bet-item" key={chip}>
                <button
                  className="betslip__free-bet-btn chip-button"
                  onClick={this.props.addButton}
                  id={chip}
                  type="button"
                >
                  {chip}
                  <div className="ribbon" id={chip}>
                    Free !
                  </div>
                </button>
              </div>
            ))}
          </div>
        );
      }
      return (
        <div className="betslip__qs">
          {this.props.odds.length > 0 && (
            <>
              <div className="betslip__qs-item">
                <button
                  type="button"
                  className="betslip__qs-btn chip-button clear"
                  onClick={this.props.clearStake}
                >
                  Clear
                </button>
              </div>
              {this.props.betslipSettings.accountOptions.map((chip) => (
                <div className="betslip__qs-item" key={chip}>
                  <button
                    type="button"
                    className="betslip__qs-btn chip-button"
                    onClick={this.props.addButton}
                    id={chip}
                  >
                    +
                    {parseNum(chip * 100)}
                  </button>
                </div>
              ))}
            </>
          )}
        </div>
      );
    }
    return null;
  };

  /**
   * Cap potential win if needed
   *
   * @function
   * @param {string} potWin
   * @param {number} winCap
   * @returns {view}
   */
  convertWin = (potWin, winCap) => {
    const formatedWinCap = parseNum(winCap);

    let win = potWin;

    if (typeof potWin === 'string') {
      if (potWin.indexOf('e+') !== -1) {
        win = winCap + 100;
      } else if (isNaN(potWin.split(',').join('')) === false) { // eslint-disable-line
        win = parseInt(potWin.split(',').join(''));
      }
    }

    if (typeof win === 'number') {
      return win > winCap / 100 ? formatedWinCap : parseNum(win);
    }
    const wins = win.split(' to ');
    const minWin = 100 * parseInt(wins[0].split(',').join(''));
    const maxWin = 100 * parseInt(wins[1].split(',').join(''));

    return `${minWin > winCap ? formatedWinCap : wins[0]} to ${maxWin > winCap ? formatedWinCap : wins[1]}`;
  }

  /**
   * Check if potential win should be capped
   *
   * @function
   * @param {string} potWin
   * @param {number} winCap
   * @returns {view}
   */
  shouldCap = (potWin, winCap) => {
    let win = potWin;

    if (typeof potWin === 'string') {
      if (potWin.indexOf('e+') !== -1) {
        return true;
      } else if (isNaN(potWin.split(',').join('')) === false) { // eslint-disable-line
        win = parseInt(potWin.split(',').join(''));
      }
    }

    if (typeof win === 'number') {
      return win > winCap / 100;
    }
    const wins = win.split(' to ');
    const minWin = 100 * parseInt(wins[0].split(',').join(''));
    const maxWin = 100 * parseInt(wins[1].split(',').join(''));

    return minWin > winCap || maxWin > winCap;
  }

  /**
   *
   * @param {object} win
   * @returns {number}
   */
  removeTOFromWin = (win) => {
    const strWin = win.toString();
    if (strWin.indexOf('to') !== -1) {
      return strWin.split(' to ')[1];
    }
    return win;
  }

  /**
   *
   * @param {number} winCap
   * @returns {view}
   */
  renderTotalWin = (winCap) => (
    <>
      {this.shouldCap(this.props.potWin, winCap) ? (
        <div className="betslip__stake-row">
          <div className="betslip__stake-pot-win betslip__stake-pot-win--green">
            {Constants.POTENTIAL_WIN_LABEL}
            <span>(CAPPED)</span>
          </div>
          <div
            className="betslip__stake-pot-win betslip__stake-pot-win--green amount"
          >
            {this.removeTOFromWin(this.convertWin(this.props.potWin, winCap))}
          </div>
        </div>
      ) : (
        <div className="betslip__stake-row">
          <div className="betslip__stake-pot-win">{Constants.POTENTIAL_WIN_LABEL}</div>
          <div className="betslip__stake-pot-win amount" id="betslipPotWinAmount">
            {this.props.totalStake === undefined && this.props.otherStake === '' ? '0' : this.removeTOFromWin(this.props.potWin)}
          </div>
        </div>
      )}
    </>
  );

  /**
   * Render place bet content
   *
   * @function
   * @param {number} winCap
   * @returns {view}
   */
  renderPlacebetContent = (winCap) => (!this.props.betSlipConfimation ? (
    <div className="betslip__stake">
      {this.props.bonusAmt && (this.props.selectedBetType !== Constants.BET_TYPE.FREEBET || this.props.otherStake !== '') && this.shouldCap(this.props.potWin, winCap) === false
        ? this.renderBonusInfo() : null}
      <div className="betslip__stake-pot">
        {!this.isFreeBet() && (
          <div className="betslip__stake-row">
            <div>Total Stake :</div>
            <div className="amount">{parseNum(this.props.totalStake * 100)}</div>
          </div>
        )}
        {this.renderTotalWin(winCap)}
      </div>
      <div className="betslip__stake-pb pr-3 pl-3">
        <button
          className={`btn btn-secondary ${this.props.isLoadingPlaceBet ? 'loading' : ''}`}
          onClick={this.handlePlacebetClick}
          id="placebet"
          disabled={this.isNobet() || this.props.placebetDisabled}
          type="button"
        >
          {this.isFreeBet() ? 'PLACE FREE BET' : 'PLACE BET'}
        </button>
      </div>
    </div>
  ) : null);

  /**
   * Render placebet error
   *
   * @function
   * @returns {view}
   */
  renderErrorMessage = () => (
    <div className="betslip__info">
      <div className="info-popup slideInRight">
        <PlacedBetsDenined
          placeBetErrorMgs={this.props.placeBetErrorMgs}
          closeError={this.props.closeError}
        />
      </div>
    </div>
  );

  renderWinCapAlert = () => (
    <div className="betslip__alert">
      <SVGComponent
        className="icon-m"
        src={`${SVG_ICONS.utility}#icon__info`}
      />
      <span>
        {'Potential Win Limit is '}
        {parseNum(this.props.winCap)}
        {' '}
        ₦
      </span>
    </div>
  )

  /**
   * Render choosen selections
   *
   * @function
   * @returns {view}
   */
  renderChoosenSelection = () => (
    <div className="betslip__content">
      <div className="betslip__heading">
        <span>Selections</span>
        {this.renderRemoveAll()}
        {this.renderCloseBetslip()}
      </div>
      {this.props.odds
        && this.props.odds.map((oddsPerMatch, i) => {
          const market = this.props.markets.find((m) => m.id === oddsPerMatch.marketId);
          const selection = getSelection(market, oddsPerMatch.selectionId);
          const match = this.props.matches[oddsPerMatch.league][oddsPerMatch.matchId];
          const matchRow = this.renderMatch(i, match, selection);
          const odd = this.roundOdd(parseFloat(oddsPerMatch.odd));
          return this.renderSelectionRow(i, null, odd, null, null, matchRow);
        })}
    </div>
  );

  /**
   * Render single tab content
   *
   * @function
   * @returns {view}
   */
  renderSingle = () => this.props.odds.map((odd, index) => {
    const match = this.props.matches[odd.league][odd.matchId];
    const market = this.props.markets.find((m) => m.id === odd.marketId);
    const selection = getSelection(market, odd.selectionId);
    const oddAndPotWin = this.props.singleStakes[index] >= this.props.betslipSettings.minStake
      ? `Pot Win ${parseNum(Math.min(odd.odd * this.props.singleStakes[index] * 100, this.props.winCap))}`
      : this.roundOdd(parseFloat(odd.odd));
    const matchRow = this.renderMatch(index, match, selection, true);
    return this.renderSelectionRow(
      index,
      null,
      oddAndPotWin,
      this.props.singleStakes[index],
      index,
      matchRow,
    );
  });

  /**
   * Render multiple tab content
   *
   * @function
   * @returns {view}
   */
  renderMultiple = () => {
    const maxPotWin = this.props.maxPotWin.length > 0
      ? this.props.maxPotWin[this.props.maxPotWin.length - 1]
      : 0;
    const maxOdd = this.props.maxOdd.length > 0
      ? this.props.maxOdd[this.props.maxOdd.length - 1]
      : 0;
    const oddAndPotWin = this.props.multipleStake >= this.props.betslipSettings.minStake
      ? `Pot Win ${parseNum(Math.min(maxPotWin * this.props.multipleStake * 100, this.props.winCap))}`
      : this.roundOdd(maxOdd);

    return this.renderSelectionRow(
      Constants.BET_TYPE.MULTIPLE,
      `${Constants.BET_TYPE.MULTIPLE} (${this.props.numberOfSelections}):`,
      oddAndPotWin,
      this.props.multipleStake,
      0,
    );
  };

  /**
   * Join min and max if they are the same
   *
   * @param {number} min
   * @param {number} max
   * @returns {string}
   */
  joinSimilarMinMax = (min, max) => {
    if (min === max) {
      return `${min}`;
    }
    return `${min} - ${max}`;
  }

  /**
   * Render split tab content
   *
   * @function
   * @returns {view}
   */
  renderSplit = () => {
    const oddAndPotWin = this.props.otherStake >= this.props.betslipSettings.minStakePerSelection
      ? `Pot Win ${this.joinSimilarMinMax(parseNum(Math.min(this.props.minPotWin[0] * this.props.otherStake * 100, this.props.winCap)), parseNum(Math.min(this.props.winCap, this.props.otherStake * this.props.maxPotWin[0] * 100)))}`
      : `${this.joinSimilarMinMax(this.roundOdd(this.props.minOdd[0]), this.roundOdd(this.props.maxOdd[0]))}`;
    return this.renderSelectionRow(
      Constants.BET_TYPE.SPLIT_COLUMN,
      `${this.props.combinationTypes[0]}:`,
      oddAndPotWin,
      this.props.otherStake,
      0,
    );
  };

  /**
   * Get default combination stakes array
   *
   * @function
   * @returns {Array}
   */
  getDefaultCombiStakes = () => {
    const stakes = [];
    this.props.combinations.map((val, index) => {
      const stakeValue = this.props.combiStakes[index] || '';
      stakes.push(stakeValue);
    });
    return stakes;
  }

  /**
   * Render combination tab content
   *
   * @function
   * @returns {view}
   */
  renderCombinations = () => {
    let oddAndPotWin = '';
    const combiStakes = this.getDefaultCombiStakes();
    return combiStakes.map((value, index) => {
      oddAndPotWin = value >= this.props.betslipSettings.minStakePerSelection
        ? this.props.minPotWin[index] === this.props.maxPotWin[index]
          ? `Pot Win ${parseNum(Math.min(this.props.winCap, this.props.minPotWin[index] * value * 100))}`
          : `Pot Win ${this.joinSimilarMinMax(parseNum(Math.min(this.props.winCap, this.props.minPotWin[index] * value * 100)), parseNum(Math.min(this.props.winCap, this.props.maxPotWin[index] * value * 100)))}`
        : this.props.minOdd[index].toFixed(2) === this.props.maxOdd[index].toFixed(2)
          ? `${this.roundOdd(this.props.minOdd[index])}`
          : `${this.roundOdd(this.props.minOdd[index])} - ${this.roundOdd(this.props.maxOdd[index])}`;
      return this.renderSelectionRow(
        this.props.combinationTypes[index],
        `${this.props.combinationTypes[index]}:`,
        oddAndPotWin,
        value,
        index,
      );
    });
  };

  /**
   * Round odd
   *
   * @function
   * @param {number} odd
   * @returns {view}
   */
  roundOdd = (odd) => (Math.round(odd * 100 + 0.000001) / 100).toFixed(2)

  /**
   * Render free bets tab content
   *
   * @function
   * @returns {view}
   */
  renderFreeBet = () => {
    let oddAndPotWin = this.props.maxOdd[this.props.maxOdd.length - 1];
    oddAndPotWin = this.roundOdd(oddAndPotWin);
    const heading = this.props.numberOfSelections === 1
      ? Constants.BET_TYPE.SINGLE
      : `${Constants.BET_TYPE.MULTIPLE} (${this.props.numberOfSelections})`;
    return this.renderSelectionRow(
      Constants.BET_TYPE.FREEBET,
      heading,
      oddAndPotWin,
      this.props.otherStake,
      0,
      null,
      true,
    );
  };
}