components/OddsTable.js


/* eslint react/no-array-index-key: 0 */
/**
 * @module OddsTable
 */
import React, { Component } from 'react';
import SVGComponent from './SVG/SVGComponent';
import ViewPlacedBets from './Modals/ViewPlacedBets';
import Constants, {
  INGNORE_ODD,
  SVG_ICONS,
  GAME_TYPE,
  MAX_SELECTIONS,
} from '../constants';
import {
  sortByHomeScores,
  sortByAwayScores,
  sortByAwayThenHomeScores,
  sortByHomeThenAwayScores,
  getSelectionNames,
  extractWinningScores,
} from '../utils/common';
import LiveMatchStatistics from './LiveMatchStatistics';

/**
 * @typedef {object} props
 * @property {boolean} isCorrectScoreSelected
 * @property {Array} markets
 * @property {Array} selectedOdds
 * @property {object} selectedLeague
 * @property {object} selectedMarket
 * @property {Array} odds
 * @property {Array} matches
 * @property {boolean} showKickOff
 * @property {string} gameType
 * @property {Function} toggleSelectedOdds
 */
export default class OddsTable extends Component {
  constructor(props) {
    super(props);
    /**
     * @member {object}
     * @property {Array} collapsedMatches Array of collapsed mathes on Correct Score market
     */
    this.state = {
      collapsedMatches: [],
      openMatchStatisticsKeys: {},
    };

    // COMPONENT WILL MOUNT
    if (props.isCorrectScoreSelected) {
      this.setupCorrectScoreTab();
    }
  }

  /**
   * Setup component if correct score market is selected
   *
   * @param {object} prevProps
   * @returns {void}
   */
  // eslint-disable-next-line react/no-deprecated
  componentDidUpdate(prevProps) {
    if (prevProps.isCorrectScoreSelected === false && this.props.isCorrectScoreSelected === true) {
      this.setupCorrectScoreTab();
    }
    if (this.props.selectedLeague.id !== prevProps.selectedLeague.id
      || this.props.gameType !== prevProps.gameType
      || this.props.roundId !== prevProps.roundId
      || this.props.selectedMarket.id !== prevProps.selectedMarket.id) {
      this.clearOpenStatisticsInfo();
    }
  }

  /**
   * Clear selected match statistics keys
   *
   * @returns {void}
   */
  clearOpenStatisticsInfo = () => {
    this.setState({ openMatchStatisticsKeys: {} });
  }

  /**
   * Devide selections of correct score on 5 arrays to show copy current template.
   * Arrays need to be sorted as it is on current template
   *
   * @function
   * @returns {void}
   */
  setupCorrectScoreTab = () => {
    const selections = this.props.markets.find((m) => m.correctScore === true).selections;
    this.awayZeroScore = [];
    this.homeWinScore = [];
    this.drawScore = [];
    this.homeZeroScore = [];
    this.awayWinScore = [];
    Object.keys(selections).map((scoreId) => {
      const score = selections[scoreId].name.split('-');
      const home = parseInt(score[0]);
      const away = parseInt(score[1]);
      const odd = { selectionId: scoreId, scoreHome: score[0], scoreAway: score[1] };
      if (home === away) {
        this.drawScore.push(odd);
      } else if (home === 0) {
        this.homeZeroScore.push(odd);
      } else if (away === 0) {
        this.awayZeroScore.push(odd);
      } else if (home > away) {
        this.homeWinScore.push(odd);
      } else {
        this.awayWinScore.push(odd);
      }
    });
    // sort
    this.awayZeroScore.sort((a, b) => sortByHomeScores(a, b));
    this.homeWinScore.sort((a, b) => (sortByAwayThenHomeScores(a, b)));
    this.drawScore.sort((a, b) => sortByHomeScores(a, b));
    this.homeZeroScore.sort((a, b) => (sortByAwayScores(a, b)));
    this.awayWinScore.sort((a, b) => (sortByHomeThenAwayScores(a, b)));
  }

  /**
   * Return odd index in selectedOdds array with passed match and selection ids
   *
   * @function
   * @param {number} match
   * @param {number} selection
   * @returns {number} Index of selected odd in array
   */
  selectedOddIndex = (match, selection) => (
    this.props.selectedOdds.findIndex((o) => o.matchId === match
      && o.selectionId === selection
      && o.league === this.props.selectedLeague.id)
  )

  /**
   * Toggle collapse/expand for passed match
   *
   * @function
   * @param {number} matchId
   * @returns {void}
   */
  toggleExpandForMatch = (matchId) => {
    const roundId = this.props.roundId;
    const leagueId = this.props.selectedLeague.id;
    const gameType = this.props.gameType;
    const stats = this.props.statistics;
    if (!stats[roundId] || !stats[roundId][leagueId]) {
      this.props.fetchStatistics({ roundId, leagueId, gameType });
    }
    const index = this.state.collapsedMatches.indexOf(matchId);
    const collapsedMatches = this.state.collapsedMatches;
    if (index !== -1) {
      collapsedMatches.splice(index, 1);
    } else {
      collapsedMatches.push(matchId);
    }
    this.setState({
      collapsedMatches: Array.from(collapsedMatches),
    });
  }

  /**
   * Toggle selection of odd
   *
   * @function
   * @param {number} matchId
   * @param {number} selectionId
   * @param {number} odd
   * @returns {void}
   */
  toggleOdds = (matchId, selectionId, odd) => {
    if (odd > INGNORE_ODD) {
      const selectedOdds = Array.from(this.props.selectedOdds);
      const index = this.selectedOddIndex(matchId, selectionId);
      if (index === -1) {
        if (selectedOdds.length < MAX_SELECTIONS) {
          selectedOdds.push({
            matchId,
            selectionId,
            marketId: this.props.selectedMarket.id,
            odd,
            winningScores: extractWinningScores(
              this.props.markets.filter((x) => x.id === this.props.selectedMarket.id)[0],
              selectionId,
            ),
            league: this.props.selectedLeague.id,
            gameType: this.props.gameType,
            roundId: this.props.roundId,
          });
        } else {
          this.props.openModal({ modal: Constants.MODALS.MAX_SELECTIONS });
          return;
        }
      } else {
        selectedOdds.splice(index, 1);
      }
      this.props.toggleSelectedOdds({ selectedOdds, index });
    }
  }

  /**
   * Render one column of odds in correct score market
   *
   * @function
   * @param {Array} selections
   * @param {Array} oddsPerMatch
   * @param {number} matchIndex
   * @returns {view}
   */
  renderCorrectScoreOddWrap = (selections, oddsPerMatch, matchIndex) => (
    <div className="market__score-wrap">
      {selections && selections.map((s) => {
        const index = this.selectedOddIndex(matchIndex, s.selectionId);
        return (
          <div
            key={s.selectionId}
            className={
              `market__score-item ${!oddsPerMatch[s.selectionId] || oddsPerMatch[s.selectionId] <= INGNORE_ODD ? Constants.CLASSES.LOCKED_SELECTION : index !== -1 ? Constants.CLASSES.SELECTED_SELECTION : ''}`
            }
            onClick={
              this.toggleOdds.bind(this, matchIndex, s.selectionId, oddsPerMatch[s.selectionId])
            }
          >
            {!oddsPerMatch[s.selectionId] || oddsPerMatch[s.selectionId] <= INGNORE_ODD ? (
              <SVGComponent
                key={this.props.selectedLeague.id}
                className="icon-m"
                src={`${SVG_ICONS.utility}#icon__locked`}
              />
            ) : (
              <>
                <div className="market__score-box--bordered">
                  {`${s.scoreHome} - ${s.scoreAway}`}
                </div>
                <div className="market__score-box">
                  {parseFloat(oddsPerMatch[s.selectionId]).toFixed(2)}
                </div>
              </>
            )}
          </div>
        );
      })}
    </div>
  )

  /**
   * Render corect score market view
   *
   * @function
   * @returns {view}
   */
  renderCorrectScore = () => (
    <div className="market">
      {this.props.odds && this.props.odds.map((oddsPerMatch, i) => {
        const league = this.props.selectedLeague.id;
        const matchId = i;
        const matchSelections = this.props.selectedOdds.filter((selOdd) => selOdd.league === league
          && selOdd.matchId === matchId);
        const hasBet = matchSelections.length > 0;

        const isCollapse = this.state.collapsedMatches.findIndex((m) => (
          m === `${this.props.selectedLeague.id}-${i}`)) === -1;
        const match = this.props.matches[i].split('-');

        const placedSelections = this.props.gameType === GAME_TYPE.CLASSIC
          ? this.props.placedSelections : this.props.placedSelectionsTurbo;

        return (
          <div className="market__accordion" key={match}>
            <div
              className={`market__accordion-head ${isCollapse ? 'collapsed' : ''} ${hasBet ? Constants.CLASSES.SELECTED_MATCH_ROW : ''}`}
              role="button"
              data-toggle="collapse"
              onClick={this.toggleExpandForMatch.bind(this, `${this.props.selectedLeague.id}-${i}`)}
            >
              <div className="market-row">
                <div className="d-flex">
                  <div className="market__team">
                    <SVGComponent
                      key={this.props.selectedLeague.id}
                      className="market__team-icon mr-3"
                      src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[0].toLowerCase()}-home`}
                    />
                    <span className="market__team-name">{match[0]}</span>
                  </div>
                  <div className="m-3">-</div>
                  <div className="market__team">
                    <span className="market__team-name">{match[1]}</span>
                    <SVGComponent
                      key={this.props.selectedLeague.id}
                      className="market__team-icon ml-3"
                      src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[1].toLowerCase()}-away`}
                    />
                  </div>
                </div>
                {this.renderMatchPlacedSelections(placedSelections, league, matchId)}
              </div>
              <div className="market__accordion-icon">
                <SVGComponent
                  className="icon-m"
                  src={`${SVG_ICONS.utility}#icon__arrow`}
                />
              </div>
            </div>
            {!isCollapse
              && (
                <div className="container-fluid market__accordion-content collapse show">
                  <div className="row market_score justify-content-between">
                    <div className="col d-flex justify-content-center">
                      {this.renderCorrectScoreOddWrap(this.awayZeroScore, oddsPerMatch, i)}
                      {this.renderCorrectScoreOddWrap(this.homeWinScore, oddsPerMatch, i)}
                    </div>
                    <div className="col d-flex justify-content-center">
                      {this.renderCorrectScoreOddWrap(this.drawScore, oddsPerMatch, i)}
                    </div>
                    <div className="col d-flex justify-content-center">
                      {this.renderCorrectScoreOddWrap(this.awayWinScore, oddsPerMatch, i)}
                      {this.renderCorrectScoreOddWrap(this.homeZeroScore, oddsPerMatch, i)}
                    </div>
                  </div>
                  <LiveMatchStatistics
                    home={match[0]}
                    away={match[1]}
                    leagueName={this.props.selectedLeague.name}
                    leagueFlag={this.props.selectedLeague.flag}
                    statistics={this.props.statistics
                      && this.props.statistics[this.props.roundId]
                      && this.props.statistics[this.props.roundId][this.props.selectedLeague.id]}
                  />
                </div>
              )}
          </div>
        );
      })}
    </div>
  )

  /**
   * Render one column of odds in correct score market
   *
   * @function
   * @param {Array} oddsPerMatch
   * @param {number} i
   * @param {string} selectedOddClass
   * @returns {view}
   */
  render1X2OverUnderSelections = (oddsPerMatch, i, selectedOddClass) => {
    const selectionMap = [[0, 1], [2, 3], [4, 5]];
    return (
      <div className="market__row-right">
        <div className="market__row-key">
          <div>O</div>
          <div>U</div>
        </div>
        {selectionMap.map((col, i1) => (
          <div className="market__odd-wrap" key={`selection-column-${i}-${i1}`}>
            {col.map((sel, i2) => {
              const selId = this.props.selectedMarket.selections[sel];
              const foundIndex = this.selectedOddIndex(i, selId);

              if (oddsPerMatch[selId] === 0 || oddsPerMatch[selId] <= INGNORE_ODD) {
                return (
                  <div className="market__odd" key={`selection-column1-${i}-${i1}-${i2}`}>
                    <SVGComponent
                      key={this.props.selectedLeague.id}
                      className="icon-m"
                      src={`${SVG_ICONS.utility}#icon__locked`}
                    />
                  </div>
                );
              }

              return (
                <div
                  key={`selection-column2-${i}-${i1}-${i2}`}
                  className={`market__odd ${foundIndex !== -1 ? selectedOddClass : ''}`}
                  onClick={this.toggleOdds.bind(this, i, selId, oddsPerMatch[selId])}
                >
                  {parseFloat(oddsPerMatch[selId]).toFixed(2)}
                </div>
              );
            })}
          </div>
        ))}
      </div>
    );
  }

  /**
   * Render one column of odds in correct score market
   *
   * @function
   * @param {Array} oddsPerMatch
   * @param {number} i index
   * @returns {view}
   */
  renderSelections = (oddsPerMatch, i) => {
    const selectedOddClass = 'market__odd--active';
    if (this.props.selectedMarket.id === Constants.MARKET_IDS.MARKET_1X2_OVER_UNDER
      || this.props.selectedMarket.id === Constants.MARKET_IDS.MARKET_DC_OVER_UNDER) {
      return this.render1X2OverUnderSelections(oddsPerMatch, i, selectedOddClass);
    }

    return (
      <>
        {this.props.selectedMarket.selections.map((key) => {
          const index = this.selectedOddIndex(i, key);
          return (
            <div
              key={key}
              className={`market__odd ${!oddsPerMatch[key] || oddsPerMatch[key] <= INGNORE_ODD
                ? 'market__odd--locked'
                : index !== -1 ? selectedOddClass : ''}`}
              onClick={this.toggleOdds.bind(this, i, key, oddsPerMatch[key])}
            >
              {!oddsPerMatch[key] || oddsPerMatch[key] <= INGNORE_ODD ? (
                <SVGComponent
                  key={this.props.selectedLeague.id}
                  className="icon-m"
                  src={`${SVG_ICONS.utility}#icon__locked`}
                />
              ) : (parseFloat(oddsPerMatch[key]).toFixed(2))}
            </div>
          );
        })}
      </>
    );
  }

  /**
   * Render one column of odds in Multigoal market
   *
   * @function
   * @param {Array} oddsPerMatch
   * @param {number} i index
   * @returns {view}
   */
  renderMultigoalSelections = (oddsPerMatch, i) => {
    const oddsMap = [[[217, 221, 225, 228], [218, 222, 226, 229]],
      [[219, 223, 227, 230], [220, 224]]];

    return (
      <div className="container-fluid market__accordion-content" key={`multigoalMatch-${i}`}>
        <div className="row market_score justify-content-between">
          {oddsMap.map((subMap1, i1) => (
            <div className="col d-flex justify-content-center" key={`multigoalMatch-${i}-${i1}`}>
              {subMap1.map((subMap2, i2) => (
                <div className="market__score-wrap" key={`multigoalMatch-${i}-${i1}-${i2}`}>
                  {subMap2.map((selectionId, i3) => {
                    const index = this.selectedOddIndex(i, selectionId);
                    const label = this.props.markets[8].selections[selectionId].name.split(' ')[1].replace('-', ' - ');
                    const oddValue = oddsPerMatch[selectionId];
                    return (
                      <div
                        className={
                          `market__score-item ${!oddValue || oddValue <= INGNORE_ODD ? Constants.CLASSES.LOCKED_SELECTION : index !== -1 ? Constants.CLASSES.SELECTED_SELECTION : ''}`
                        }
                        onClick={
                          this.toggleOdds.bind(this, i, selectionId, oddValue)
                        }
                        key={`multigoalMatch-${i}-${i1}-${i2}-${i3}`}
                      >
                        <div className="market__score-box--bordered">{label}</div>
                        <div className="market__score-box">{parseFloat(oddValue).toFixed(2)}</div>
                      </div>
                    );
                  })}
                </div>
              ))}
            </div>
          ))}
        </div>
      </div>
    );
  }

  /**
   * Render odds table view
   *
   * @function
   * @returns {view}
   */
  renderOddsTable = () => (
    <div className="markets">
      {this.props.odds && this.props.odds.map((oddsPerMatch, i) => {
        const match = this.props.matches[i].split('-');
        const league = this.props.selectedLeague.id;
        const matchId = i;
        const isStatsOpen = this.state.openMatchStatisticsKeys[`${this.props.gameType}-${league}-${matchId}`];
        const matchSelections = this.props.selectedOdds.filter((selOdd) => selOdd.league === league
          && selOdd.matchId === matchId);
        const hasBet = matchSelections.length > 0;

        const placedSelections = this.props.gameType === GAME_TYPE.CLASSIC
          ? this.props.placedSelections : this.props.placedSelectionsTurbo;

        return (
          <div className={`market__row ${hasBet ? Constants.CLASSES.SELECTED_MATCH_ROW : ''}`} key={match}>
            <div className="market__row-inner">
              <div className="market__row-left">
                <div className="market__row-wrap">
                  <div className="d-flex align-items-center">
                    <div
                      onClick={() => this.toogleMatchStatistics(`${this.props.gameType}-${league}-${matchId}`)}
                      className="market__statistic-icon mr-3"
                    />
                    <div className="market__team">
                      <SVGComponent
                        key={this.props.selectedLeague.id}
                        className="market__team-icon mr-3"
                        src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[0].toLowerCase()}-home`}
                      />
                      <span className="market__team-name">
                        {match[0]}
                      </span>
                    </div>
                    <div className="m-3">-</div>
                    <div className="market__team">
                      <span className="market__team-name">
                        {match[1]}
                      </span>
                      <SVGComponent
                        key={this.props.selectedLeague.id}
                        className="market__team-icon ml-3"
                        src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[1].toLowerCase()}-away`}
                      />
                    </div>
                  </div>
                  {this.renderMatchPlacedSelections(placedSelections, league, matchId)}
                </div>
              </div>
              <div className="market__row-right">
                {this.renderSelections(oddsPerMatch, i)}
              </div>
            </div>
            {isStatsOpen
              && (
                <LiveMatchStatistics
                  home={match[0]}
                  away={match[1]}
                  leagueName={this.props.selectedLeague.name}
                  leagueFlag={this.props.selectedLeague.flag}
                  statistics={this.props.statistics
                    && this.props.statistics[this.props.roundId]
                    && this.props.statistics[this.props.roundId][this.props.selectedLeague.id]}
                />
              )}
          </div>
        );
      })}
    </div>
  )

  /**
   * Render odds for Multigoal market
   *
   * @function
   * @returns {view}
   */
  renderMultiGoalOdds = () => (
    <div className="market">
      {this.props.odds && this.props.odds.map((oddsPerMatch, i) => {
        const match = this.props.matches[i].split('-');
        const league = this.props.selectedLeague.id;
        const matchId = i;
        const matchSelections = this.props.selectedOdds.filter((selOdd) => selOdd.league === league
          && selOdd.matchId === matchId);
        const hasBet = matchSelections.length > 0;
        const isStatsOpen = this.state.openMatchStatisticsKeys[`${this.props.gameType}-${league}-${matchId}`];

        const placedSelections = this.props.gameType === GAME_TYPE.CLASSIC
          ? this.props.placedSelections : this.props.placedSelectionsTurbo;

        return (
          <div className="market__accordion" key={`marketMultigoalOdds-${i}`}>
            <div className={`market__accordion-head ${hasBet ? Constants.CLASSES.SELECTED_MATCH_ROW : ''}`}>
              <div className="d-flex">
                <div
                  onClick={() => this.toogleMatchStatistics(`${this.props.gameType}-${league}-${matchId}`)}
                  className="market__statistic-icon mr-3"
                />
                <div className="market__team">
                  <SVGComponent
                    key={this.props.selectedLeague.id}
                    className="market__team-icon mr-3"
                    src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[0].toLowerCase()}-home`}
                  />
                  <span className="market__team-name">
                    {match[0]}
                  </span>
                </div>
                <div className="m-3">-</div>
                <div className="market__team">
                  <span className="market__team-name">
                    {match[1]}
                  </span>
                  <SVGComponent
                    key={this.props.selectedLeague.id}
                    className="market__team-icon ml-3"
                    src={`${SVG_ICONS.shirts}${this.props.selectedLeague.name.toLowerCase().replace(/\s/g, '')}.svg#${this.props.selectedLeague.flag}-${match[1].toLowerCase()}-away`}
                  />
                </div>
              </div>
              {this.renderMatchPlacedSelections(placedSelections, league, matchId)}
            </div>
            {this.renderMultigoalSelections(oddsPerMatch, i)}
            {isStatsOpen
              && (
                <LiveMatchStatistics
                  home={match[0]}
                  away={match[1]}
                  leagueName={this.props.selectedLeague.name}
                  leagueFlag={this.props.selectedLeague.flag}
                  statistics={this.props.statistics
                    && this.props.statistics[this.props.roundId]
                    && this.props.statistics[this.props.roundId][this.props.selectedLeague.id]}
                />
              )}
          </div>
        );
      })}
    </div>
  )

  /**
   * Render placed selections for a match
   *
   * @function
   * @param {object} placedSelections
   * @param {number} league
   * @param {number} matchId
   * @returns {view}
   */
  renderMatchPlacedSelections = (placedSelections, league, matchId) => {
    const matchPlacedSelection = getSelectionNames(placedSelections, league, matchId);
    return (<div className="txt-cut market__odd-info">{matchPlacedSelection}</div>);
  }

  /**
   * Render view placed bet modal
   *
   * @function
   * @returns {view}
   */
  renderViewPLacedModal = () => (
    this.props.activeModal === Constants.MODALS.VIEW_PLACED_BETS
      ? (
        <ViewPlacedBets
          closeModal={this.props.closeModal}
          changePage={this.props.changePage}
          currentPage={this.props.currentPage}
        />
      ) : null
  );

  /**
   * Render Odds
   *
   * @function
   * @returns {view}
   */
  renderOdds = () => {
    if (this.props.isCorrectScoreSelected) {
      return this.renderCorrectScore();
    }
    if (this.props.selectedMarket.id === Constants.MARKET_IDS.MARKET_MULTIGOAL) {
      return this.renderMultiGoalOdds();
    }
    return this.renderOddsTable();
  }

  /**
   * Toggle statistics
   *
   * @function
   * @param {string} key row key
   * @returns {void}
   */
  toogleMatchStatistics = (key) => {
    const roundId = this.props.roundId;
    const leagueId = this.props.selectedLeague.id;
    const gameType = this.props.gameType;
    const stats = this.props.statistics;
    const keyMap = this.state.openMatchStatisticsKeys;
    if (keyMap[key] !== true) {
      if (!stats[roundId] || !stats[roundId][leagueId]) {
        this.props.fetchStatistics({ roundId, leagueId, gameType });
      }
      keyMap[key] = true;
    } else {
      keyMap[key] = false;
    }
    this.setState({ openMatchStatisticsKeys: keyMap });
  }

  /**
   * Render
   *
   * @returns {view}
   */
  render() {
    return (
      <>
        <div className="scroll-area">
          {this.renderOdds()}
        </div>
        {this.renderViewPLacedModal()}
        {this.props.showKickOff && this.props.gameType === GAME_TYPE.TURBO
          && (
            <div className="odds-btn text-center">
              <button
                type="button"
                className="btn btn-bordered btn-sm"
                onClick={this.props.kickOff}
                disabled={this.props.haveLiveMatches}
              >
                Kick Off !
              </button>
            </div>
          )}
      </>
    );
  }
}