/* 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>
)}
</>
);
}
}