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